/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: v.ladeuil+lp at free
  • Date: 2007-01-08 10:21:20 UTC
  • mfrom: (2225 +trunk)
  • mto: (2323.7.1 redirection)
  • mto: This revision was merged to the branch mainline in revision 2390.
  • Revision ID: v.ladeuil+lp@free.fr-20070108102120-rhp9vvuhmshrrd92
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
 
39
39
from cStringIO import StringIO
40
40
import os
41
 
import re
42
41
 
43
42
from bzrlib.lazy_import import lazy_import
44
43
lazy_import(globals(), """
45
44
import collections
46
45
from copy import deepcopy
47
46
import errno
48
 
import fnmatch
49
47
import stat
50
48
from time import time
51
49
import warnings
56
54
    conflicts as _mod_conflicts,
57
55
    errors,
58
56
    generate_ids,
 
57
    globbing,
 
58
    hashcache,
59
59
    ignores,
60
60
    merge,
61
61
    osutils,
103
103
from bzrlib.transport.local import LocalTransport
104
104
import bzrlib.tree
105
105
from bzrlib.progress import DummyProgress, ProgressPhase
106
 
from bzrlib.revision import NULL_REVISION
 
106
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
107
107
import bzrlib.revisiontree
108
108
from bzrlib.rio import RioReader, rio_file, Stanza
109
109
from bzrlib.symbol_versioning import (deprecated_passed,
233
233
            self._set_inventory(wt._inventory, dirty=False)
234
234
            self._format = wt._format
235
235
            self.bzrdir = wt.bzrdir
236
 
        from bzrlib.hashcache import HashCache
237
 
        from bzrlib.trace import note, mutter
238
236
        assert isinstance(basedir, basestring), \
239
237
            "base directory %r is not a string" % basedir
240
238
        basedir = safe_unicode(basedir)
268
266
        # cache file, and have the parser take the most recent entry for a
269
267
        # given path only.
270
268
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
271
 
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
 
269
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
 
270
                                              self._control_files._file_mode)
 
271
        hc = self._hashcache
272
272
        hc.read()
273
273
        # is this scan needed ? it makes things kinda slow.
274
274
        #hc.scan()
469
469
    def get_file_byname(self, filename):
470
470
        return file(self.abspath(filename), 'rb')
471
471
 
 
472
    def annotate_iter(self, file_id):
 
473
        """See Tree.annotate_iter
 
474
 
 
475
        This implementation will use the basis tree implementation if possible.
 
476
        Lines not in the basis are attributed to CURRENT_REVISION
 
477
 
 
478
        If there are pending merges, lines added by those merges will be
 
479
        incorrectly attributed to CURRENT_REVISION (but after committing, the
 
480
        attribution will be correct).
 
481
        """
 
482
        basis = self.basis_tree()
 
483
        changes = self._iter_changes(basis, True, [file_id]).next()
 
484
        changed_content, kind = changes[2], changes[6]
 
485
        if not changed_content:
 
486
            return basis.annotate_iter(file_id)
 
487
        if kind[1] is None:
 
488
            return None
 
489
        import annotate
 
490
        if kind[0] != 'file':
 
491
            old_lines = []
 
492
        else:
 
493
            old_lines = list(basis.annotate_iter(file_id))
 
494
        old = [old_lines]
 
495
        for tree in self.branch.repository.revision_trees(
 
496
            self.get_parent_ids()[1:]):
 
497
            if file_id not in tree:
 
498
                continue
 
499
            old.append(list(tree.annotate_iter(file_id)))
 
500
        return annotate.reannotate(old, self.get_file(file_id).readlines(),
 
501
                                   CURRENT_REVISION)
 
502
 
472
503
    def get_parent_ids(self):
473
504
        """See Tree.get_parent_ids.
474
505
        
1223
1254
                subp = pathjoin(path, subf)
1224
1255
                yield subp
1225
1256
 
1226
 
    def _translate_ignore_rule(self, rule):
1227
 
        """Translate a single ignore rule to a regex.
1228
 
 
1229
 
        There are two types of ignore rules.  Those that do not contain a / are
1230
 
        matched against the tail of the filename (that is, they do not care
1231
 
        what directory the file is in.)  Rules which do contain a slash must
1232
 
        match the entire path.  As a special case, './' at the start of the
1233
 
        string counts as a slash in the string but is removed before matching
1234
 
        (e.g. ./foo.c, ./src/foo.c)
1235
 
 
1236
 
        :return: The translated regex.
1237
 
        """
1238
 
        if rule[:2] in ('./', '.\\'):
1239
 
            # rootdir rule
1240
 
            result = fnmatch.translate(rule[2:])
1241
 
        elif '/' in rule or '\\' in rule:
1242
 
            # path prefix 
1243
 
            result = fnmatch.translate(rule)
1244
 
        else:
1245
 
            # default rule style.
1246
 
            result = "(?:.*/)?(?!.*/)" + fnmatch.translate(rule)
1247
 
        assert result[-1] == '$', "fnmatch.translate did not add the expected $"
1248
 
        return "(" + result + ")"
1249
 
 
1250
 
    def _combine_ignore_rules(self, rules):
1251
 
        """Combine a list of ignore rules into a single regex object.
1252
 
 
1253
 
        Each individual rule is combined with | to form a big regex, which then
1254
 
        has $ added to it to form something like ()|()|()$. The group index for
1255
 
        each subregex's outermost group is placed in a dictionary mapping back 
1256
 
        to the rule. This allows quick identification of the matching rule that
1257
 
        triggered a match.
1258
 
        :return: a list of the compiled regex and the matching-group index 
1259
 
        dictionaries. We return a list because python complains if you try to 
1260
 
        combine more than 100 regexes.
1261
 
        """
1262
 
        result = []
1263
 
        groups = {}
1264
 
        next_group = 0
1265
 
        translated_rules = []
1266
 
        for rule in rules:
1267
 
            translated_rule = self._translate_ignore_rule(rule)
1268
 
            compiled_rule = re.compile(translated_rule)
1269
 
            groups[next_group] = rule
1270
 
            next_group += compiled_rule.groups
1271
 
            translated_rules.append(translated_rule)
1272
 
            if next_group == 99:
1273
 
                result.append((re.compile("|".join(translated_rules)), groups))
1274
 
                groups = {}
1275
 
                next_group = 0
1276
 
                translated_rules = []
1277
 
        if len(translated_rules):
1278
 
            result.append((re.compile("|".join(translated_rules)), groups))
1279
 
        return result
1280
1257
 
1281
1258
    def ignored_files(self):
1282
1259
        """Yield list of PATH, IGNORE_PATTERN"""
1296
1273
 
1297
1274
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
1298
1275
        ignore_globs.update(ignores.get_runtime_ignores())
1299
 
 
1300
1276
        ignore_globs.update(ignores.get_user_ignores())
1301
 
 
1302
1277
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1303
1278
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1304
1279
            try:
1305
1280
                ignore_globs.update(ignores.parse_ignore_file(f))
1306
1281
            finally:
1307
1282
                f.close()
1308
 
 
1309
1283
        self._ignoreset = ignore_globs
1310
 
        self._ignore_regex = self._combine_ignore_rules(ignore_globs)
1311
1284
        return ignore_globs
1312
1285
 
1313
 
    def _get_ignore_rules_as_regex(self):
1314
 
        """Return a regex of the ignore rules and a mapping dict.
1315
 
 
1316
 
        :return: (ignore rules compiled regex, dictionary mapping rule group 
1317
 
        indices to original rule.)
1318
 
        """
1319
 
        if getattr(self, '_ignoreset', None) is None:
1320
 
            self.get_ignore_list()
1321
 
        return self._ignore_regex
 
1286
    def _flush_ignore_list_cache(self):
 
1287
        """Resets the cached ignore list to force a cache rebuild."""
 
1288
        self._ignoreset = None
 
1289
        self._ignoreglobster = None
1322
1290
 
1323
1291
    def is_ignored(self, filename):
1324
1292
        r"""Check whether the filename matches an ignore pattern.
1329
1297
        If the file is ignored, returns the pattern which caused it to
1330
1298
        be ignored, otherwise None.  So this can simply be used as a
1331
1299
        boolean if desired."""
1332
 
 
1333
 
        # TODO: Use '**' to match directories, and other extended
1334
 
        # globbing stuff from cvs/rsync.
1335
 
 
1336
 
        # XXX: fnmatch is actually not quite what we want: it's only
1337
 
        # approximately the same as real Unix fnmatch, and doesn't
1338
 
        # treat dotfiles correctly and allows * to match /.
1339
 
        # Eventually it should be replaced with something more
1340
 
        # accurate.
1341
 
    
1342
 
        rules = self._get_ignore_rules_as_regex()
1343
 
        for regex, mapping in rules:
1344
 
            match = regex.match(filename)
1345
 
            if match is not None:
1346
 
                # one or more of the groups in mapping will have a non-None
1347
 
                # group match.
1348
 
                groups = match.groups()
1349
 
                rules = [mapping[group] for group in 
1350
 
                    mapping if groups[group] is not None]
1351
 
                return rules[0]
1352
 
        return None
 
1300
        if getattr(self, '_ignoreglobster', None) is None:
 
1301
            self._ignoreglobster = globbing.Globster(self.get_ignore_list())
 
1302
        return self._ignoreglobster.match(filename)
1353
1303
 
1354
1304
    def kind(self, file_id):
1355
1305
        return file_kind(self.id2abspath(file_id))
1749
1699
                                  this_tree=self)
1750
1700
        return result
1751
1701
 
 
1702
    def _write_hashcache_if_dirty(self):
 
1703
        """Write out the hashcache if it is dirty."""
 
1704
        if self._hashcache.needs_write:
 
1705
            try:
 
1706
                self._hashcache.write()
 
1707
            except OSError, e:
 
1708
                if e.errno not in (errno.EPERM, errno.EACCES):
 
1709
                    raise
 
1710
                # TODO: jam 20061219 Should this be a warning? A single line
 
1711
                #       warning might be sufficient to let the user know what
 
1712
                #       is going on.
 
1713
                mutter('Could not write hashcache for %s\nError: %s',
 
1714
                       self._hashcache.cache_file_name(), e)
 
1715
 
1752
1716
    @needs_tree_write_lock
1753
1717
    def _write_inventory(self, inv):
1754
1718
        """Write inventory as the current inventory."""
1815
1779
            # _inventory_is_modified is always False during a read lock.
1816
1780
            if self._inventory_is_modified:
1817
1781
                self.flush()
1818
 
            if self._hashcache.needs_write:
1819
 
                self._hashcache.write()
 
1782
            self._write_hashcache_if_dirty()
 
1783
                    
1820
1784
        # reverse order of locking.
1821
1785
        try:
1822
1786
            return self._control_files.unlock()
1884
1848
            # _inventory_is_modified is always False during a read lock.
1885
1849
            if self._inventory_is_modified:
1886
1850
                self.flush()
1887
 
            if self._hashcache.needs_write:
1888
 
                self._hashcache.write()
 
1851
            self._write_hashcache_if_dirty()
1889
1852
        # reverse order of locking.
1890
1853
        try:
1891
1854
            return self._control_files.unlock()