/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

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
51
51
from time import time
52
52
import warnings
53
53
 
 
54
import bzrlib
 
55
from bzrlib import bzrdir, errors, ignores, osutils, urlutils
54
56
from bzrlib.atomicfile import AtomicFile
 
57
import bzrlib.branch
55
58
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
56
 
import bzrlib.bzrdir as bzrdir
57
59
from bzrlib.decorators import needs_read_lock, needs_write_lock
58
 
import bzrlib.errors as errors
59
60
from bzrlib.errors import (BzrCheckError,
60
61
                           BzrError,
61
62
                           ConflictFormatError,
96
97
        DEPRECATED_PARAMETER,
97
98
        zero_eight,
98
99
        )
99
 
 
100
 
from bzrlib.textui import show_status
101
 
import bzrlib.tree
 
100
from bzrlib.trace import mutter, note
102
101
from bzrlib.transform import build_tree
103
 
from bzrlib.trace import mutter, note
104
102
from bzrlib.transport import get_transport
105
103
from bzrlib.transport.local import LocalTransport
106
 
import bzrlib.urlutils as urlutils
 
104
from bzrlib.textui import show_status
 
105
import bzrlib.tree
107
106
import bzrlib.ui
108
107
import bzrlib.xml5
109
108
 
110
109
 
111
 
# the regex here does the following:
112
 
# 1) remove any weird characters; we don't escape them but rather
113
 
# just pull them out
114
 
 # 2) match leading '.'s to make it not hidden
115
 
_gen_file_id_re = re.compile(r'[^\w.]|(^\.*)')
 
110
# the regex removes any weird characters; we don't escape them 
 
111
# but rather just pull them out
 
112
_gen_file_id_re = re.compile(r'[^\w.]')
116
113
_gen_id_suffix = None
117
114
_gen_id_serial = 0
118
115
 
140
137
 
141
138
    The uniqueness is supplied from _next_id_suffix.
142
139
    """
143
 
    # XXX TODO: squash the filename to lowercase.
144
 
    # XXX TODO: truncate the filename to something like 20 or 30 chars.
145
 
    # XXX TODO: consider what to do with ids that look like illegal filepaths
146
 
    # on platforms we support.
147
 
    return _gen_file_id_re.sub('', name) + _next_id_suffix()
 
140
    # The real randomness is in the _next_id_suffix, the
 
141
    # rest of the identifier is just to be nice.
 
142
    # So we:
 
143
    # 1) Remove non-ascii word characters to keep the ids portable
 
144
    # 2) squash to lowercase, so the file id doesn't have to
 
145
    #    be escaped (case insensitive filesystems would bork for ids
 
146
    #    that only differred in case without escaping).
 
147
    # 3) truncate the filename to 20 chars. Long filenames also bork on some
 
148
    #    filesystems
 
149
    # 4) Removing starting '.' characters to prevent the file ids from
 
150
    #    being considered hidden.
 
151
    ascii_word_only = _gen_file_id_re.sub('', name.lower())
 
152
    short_no_dots = ascii_word_only.lstrip('.')[:20]
 
153
    return short_no_dots + _next_id_suffix()
148
154
 
149
155
 
150
156
def gen_root_id():
270
276
            # share control object
271
277
            self._control_files = self.branch.control_files
272
278
        else:
273
 
            # only ready for format 3
274
 
            assert isinstance(self._format, WorkingTreeFormat3)
 
279
            # assume all other formats have their own control files.
275
280
            assert isinstance(_control_files, LockableFiles), \
276
281
                    "_control_files must be a LockableFiles, not %r" \
277
282
                    % _control_files
356
361
        :return: The WorkingTree that contains 'path', and the rest of path
357
362
        """
358
363
        if path is None:
359
 
            path = os.getcwdu()
 
364
            path = osutils.getcwd()
360
365
        control, relpath = bzrdir.BzrDir.open_containing(path)
361
366
 
362
367
        return control.open_workingtree(), relpath
377
382
        """
378
383
        inv = self._inventory
379
384
        for path, ie in inv.iter_entries():
380
 
            if bzrlib.osutils.lexists(self.abspath(path)):
 
385
            if osutils.lexists(self.abspath(path)):
381
386
                yield ie.file_id
382
387
 
383
388
    def __repr__(self):
449
454
        return relpath(self.basedir, path)
450
455
 
451
456
    def has_filename(self, filename):
452
 
        return bzrlib.osutils.lexists(self.abspath(filename))
 
457
        return osutils.lexists(self.abspath(filename))
453
458
 
454
459
    def get_file(self, file_id):
455
460
        return self.get_file_byname(self.id2path(file_id))
456
461
 
 
462
    def get_file_text(self, file_id):
 
463
        return self.get_file(file_id).read()
 
464
 
457
465
    def get_file_byname(self, filename):
458
466
        return file(self.abspath(filename), 'rb')
459
467
 
540
548
        if not inv.has_id(file_id):
541
549
            return False
542
550
        path = inv.id2path(file_id)
543
 
        return bzrlib.osutils.lexists(self.abspath(path))
 
551
        return osutils.lexists(self.abspath(path))
544
552
 
545
553
    def has_or_had_id(self, file_id):
546
554
        if file_id == self.inventory.root.file_id:
724
732
        """
725
733
        inv = self._inventory
726
734
        # Convert these into local objects to save lookup times
727
 
        pathjoin = bzrlib.osutils.pathjoin
728
 
        file_kind = bzrlib.osutils.file_kind
 
735
        pathjoin = osutils.pathjoin
 
736
        file_kind = osutils.file_kind
729
737
 
730
738
        # transport.base ends in a slash, we want the piece
731
739
        # between the last two slashes
771
779
                elif self.is_ignored(fp[1:]):
772
780
                    c = 'I'
773
781
                else:
774
 
                    c = '?'
 
782
                    # we may not have found this file, because of a unicode issue
 
783
                    f_norm, can_access = osutils.normalized_filename(f)
 
784
                    if f == f_norm or not can_access:
 
785
                        # No change, so treat this file normally
 
786
                        c = '?'
 
787
                    else:
 
788
                        # this file can be accessed by a normalized path
 
789
                        # check again if it is versioned
 
790
                        # these lines are repeated here for performance
 
791
                        f = f_norm
 
792
                        fp = from_dir_relpath + '/' + f
 
793
                        fap = from_dir_abspath + '/' + f
 
794
                        f_ie = inv.get_child(from_dir_id, f)
 
795
                        if f_ie:
 
796
                            c = 'V'
 
797
                        elif self.is_ignored(fp[1:]):
 
798
                            c = 'I'
 
799
                        else:
 
800
                            c = '?'
775
801
 
776
802
                fk = file_kind(fap)
777
803
 
996
1022
        """
997
1023
        ## TODO: Work from given directory downwards
998
1024
        for path, dir_entry in self.inventory.directories():
999
 
            mutter("search for unknowns in %r", path)
 
1025
            # mutter("search for unknowns in %r", path)
1000
1026
            dirabs = self.abspath(path)
1001
1027
            if not isdir(dirabs):
1002
1028
                # e.g. directory deleted
1004
1030
 
1005
1031
            fl = []
1006
1032
            for subf in os.listdir(dirabs):
1007
 
                if (subf != '.bzr'
1008
 
                    and (subf not in dir_entry.children)):
1009
 
                    fl.append(subf)
 
1033
                if subf == '.bzr':
 
1034
                    continue
 
1035
                if subf not in dir_entry.children:
 
1036
                    subf_norm, can_access = osutils.normalized_filename(subf)
 
1037
                    if subf_norm != subf and can_access:
 
1038
                        if subf_norm not in dir_entry.children:
 
1039
                            fl.append(subf_norm)
 
1040
                    else:
 
1041
                        fl.append(subf)
1010
1042
            
1011
1043
            fl.sort()
1012
1044
            for subf in fl:
1080
1112
 
1081
1113
        Cached in the Tree object after the first call.
1082
1114
        """
1083
 
        if hasattr(self, '_ignorelist'):
1084
 
            return self._ignorelist
1085
 
 
1086
 
        l = []
 
1115
        ignoreset = getattr(self, '_ignoreset', None)
 
1116
        if ignoreset is not None:
 
1117
            return ignoreset
 
1118
 
 
1119
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
 
1120
        ignore_globs.update(ignores.get_runtime_ignores())
 
1121
 
 
1122
        ignore_globs.update(ignores.get_user_ignores())
 
1123
 
1087
1124
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1088
1125
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1089
 
            l.extend([line.rstrip("\n\r").decode('utf-8') 
1090
 
                      for line in f.readlines()])
1091
 
        self._ignorelist = l
1092
 
        self._ignore_regex = self._combine_ignore_rules(l)
1093
 
        return l
 
1126
            try:
 
1127
                ignore_globs.update(ignores.parse_ignore_file(f))
 
1128
            finally:
 
1129
                f.close()
 
1130
 
 
1131
        self._ignoreset = ignore_globs
 
1132
        self._ignore_regex = self._combine_ignore_rules(ignore_globs)
 
1133
        return ignore_globs
1094
1134
 
1095
1135
    def _get_ignore_rules_as_regex(self):
1096
1136
        """Return a regex of the ignore rules and a mapping dict.
1098
1138
        :return: (ignore rules compiled regex, dictionary mapping rule group 
1099
1139
        indices to original rule.)
1100
1140
        """
1101
 
        if getattr(self, '_ignorelist', None) is None:
 
1141
        if getattr(self, '_ignoreset', None) is None:
1102
1142
            self.get_ignore_list()
1103
1143
        return self._ignore_regex
1104
1144
 
1344
1384
        between multiple working trees, i.e. via shared storage, then we 
1345
1385
        would probably want to lock both the local tree, and the branch.
1346
1386
        """
1347
 
        # FIXME: We want to write out the hashcache only when the last lock on
1348
 
        # this working copy is released.  Peeking at the lock count is a bit
1349
 
        # of a nasty hack; probably it's better to have a transaction object,
1350
 
        # which can do some finalization when it's either successfully or
1351
 
        # unsuccessfully completed.  (Denys's original patch did that.)
1352
 
        # RBC 20060206 hooking into transaction will couple lock and transaction
1353
 
        # wrongly. Hooking into unlock on the control files object is fine though.
1354
 
        
1355
 
        # TODO: split this per format so there is no ugly if block
1356
 
        if self._hashcache.needs_write and (
1357
 
            # dedicated lock files
1358
 
            self._control_files._lock_count==1 or 
1359
 
            # shared lock files
1360
 
            (self._control_files is self.branch.control_files and 
1361
 
             self._control_files._lock_count==3)):
1362
 
            self._hashcache.write()
1363
 
        # reverse order of locking.
1364
 
        try:
1365
 
            return self._control_files.unlock()
1366
 
        finally:
1367
 
            self.branch.unlock()
 
1387
        raise NotImplementedError(self.unlock)
1368
1388
 
1369
1389
    @needs_write_lock
1370
1390
    def update(self):
1473
1493
        return conflicts
1474
1494
 
1475
1495
 
 
1496
class WorkingTree2(WorkingTree):
 
1497
    """This is the Format 2 working tree.
 
1498
 
 
1499
    This was the first weave based working tree. 
 
1500
     - uses os locks for locking.
 
1501
     - uses the branch last-revision.
 
1502
    """
 
1503
 
 
1504
    def unlock(self):
 
1505
        # we share control files:
 
1506
        if self._hashcache.needs_write and self._control_files._lock_count==3:
 
1507
            self._hashcache.write()
 
1508
        # reverse order of locking.
 
1509
        try:
 
1510
            return self._control_files.unlock()
 
1511
        finally:
 
1512
            self.branch.unlock()
 
1513
 
 
1514
 
1476
1515
class WorkingTree3(WorkingTree):
1477
1516
    """This is the Format 3 working tree.
1478
1517
 
1532
1571
            raise ConflictFormatError()
1533
1572
        return ConflictList.from_stanzas(RioReader(confile))
1534
1573
 
 
1574
    def unlock(self):
 
1575
        if self._hashcache.needs_write and self._control_files._lock_count==1:
 
1576
            self._hashcache.write()
 
1577
        # reverse order of locking.
 
1578
        try:
 
1579
            return self._control_files.unlock()
 
1580
        finally:
 
1581
            self.branch.unlock()
 
1582
 
1535
1583
 
1536
1584
def get_conflicted_stem(path):
1537
1585
    for suffix in CONFLICT_SUFFIXES:
1671
1719
                branch.unlock()
1672
1720
        revision = branch.last_revision()
1673
1721
        inv = Inventory(root_id=gen_root_id()) 
1674
 
        wt = WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
 
1722
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
1675
1723
                         branch,
1676
1724
                         inv,
1677
1725
                         _internal=True,
1701
1749
            raise NotImplementedError
1702
1750
        if not isinstance(a_bzrdir.transport, LocalTransport):
1703
1751
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1704
 
        return WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
 
1752
        return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
1705
1753
                           _internal=True,
1706
1754
                           _format=self,
1707
1755
                           _bzrdir=a_bzrdir)
1716
1764
          files, separate from the BzrDir format
1717
1765
        - modifies the hash cache format
1718
1766
        - is new in bzr 0.8
1719
 
        - uses a LockDir to guard access to the repository
 
1767
        - uses a LockDir to guard access for writes.
1720
1768
    """
1721
1769
 
1722
1770
    def get_format_string(self):
1788
1836
            raise NotImplementedError
1789
1837
        if not isinstance(a_bzrdir.transport, LocalTransport):
1790
1838
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1791
 
        control_files = self._open_control_files(a_bzrdir)
 
1839
        return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1840
 
 
1841
    def _open(self, a_bzrdir, control_files):
 
1842
        """Open the tree itself.
 
1843
        
 
1844
        :param a_bzrdir: the dir for the tree.
 
1845
        :param control_files: the control files for the tree.
 
1846
        """
1792
1847
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1793
1848
                           _internal=True,
1794
1849
                           _format=self,
1822
1877
        self._transport_readonly_server = transport_readonly_server
1823
1878
        self._formats = formats
1824
1879
    
 
1880
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
 
1881
        """Clone test for adaption."""
 
1882
        new_test = deepcopy(test)
 
1883
        new_test.transport_server = self._transport_server
 
1884
        new_test.transport_readonly_server = self._transport_readonly_server
 
1885
        new_test.bzrdir_format = bzrdir_format
 
1886
        new_test.workingtree_format = workingtree_format
 
1887
        def make_new_test_id():
 
1888
            new_id = "%s(%s)" % (test.id(), variation)
 
1889
            return lambda: new_id
 
1890
        new_test.id = make_new_test_id()
 
1891
        return new_test
 
1892
    
1825
1893
    def adapt(self, test):
1826
1894
        from bzrlib.tests import TestSuite
1827
1895
        result = TestSuite()
1828
1896
        for workingtree_format, bzrdir_format in self._formats:
1829
 
            new_test = deepcopy(test)
1830
 
            new_test.transport_server = self._transport_server
1831
 
            new_test.transport_readonly_server = self._transport_readonly_server
1832
 
            new_test.bzrdir_format = bzrdir_format
1833
 
            new_test.workingtree_format = workingtree_format
1834
 
            def make_new_test_id():
1835
 
                new_id = "%s(%s)" % (new_test.id(), workingtree_format.__class__.__name__)
1836
 
                return lambda: new_id
1837
 
            new_test.id = make_new_test_id()
 
1897
            new_test = self._clone_test(
 
1898
                test,
 
1899
                bzrdir_format,
 
1900
                workingtree_format, workingtree_format.__class__.__name__)
1838
1901
            result.addTest(new_test)
1839
1902
        return result