/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: Martin Pool
  • Date: 2006-06-10 23:16:19 UTC
  • mfrom: (1759 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1761.
  • Revision ID: mbp@sourcefrog.net-20060610231619-05b997deeb005d02
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
40
40
# memory.  (Now done? -- mbp 20060309)
41
41
 
42
42
from binascii import hexlify
 
43
import collections
43
44
from copy import deepcopy
44
45
from cStringIO import StringIO
45
46
import errno
73
74
from bzrlib.merge import merge_inner, transform_tree
74
75
from bzrlib.osutils import (
75
76
                            abspath,
76
 
                            appendpath,
77
77
                            compact_date,
78
78
                            file_kind,
79
79
                            isdir,
99
99
from bzrlib.trace import mutter, note
100
100
from bzrlib.transport import get_transport
101
101
from bzrlib.transport.local import LocalTransport
 
102
import bzrlib.urlutils as urlutils
102
103
import bzrlib.ui
103
104
import bzrlib.xml5
104
105
 
279
280
        # if needed, or, when the cache sees a change, append it to the hash
280
281
        # cache file, and have the parser take the most recent entry for a
281
282
        # given path only.
282
 
        cache_filename = self.bzrdir.get_workingtree_transport(None).abspath('stat-cache')
 
283
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
283
284
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
284
285
        hc.read()
285
286
        # is this scan needed ? it makes things kinda slow.
286
 
        hc.scan()
 
287
        #hc.scan()
287
288
 
288
289
        if hc.needs_write:
289
290
            mutter("write hc")
349
350
        run into /.  If there isn't one, raises NotBranchError.
350
351
        TODO: give this a new exception.
351
352
        If there is one, it is returned, along with the unused portion of path.
 
353
 
 
354
        :return: The WorkingTree that contains 'path', and the rest of path
352
355
        """
353
356
        if path is None:
354
357
            path = os.getcwdu()
355
358
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
359
 
356
360
        return control.open_workingtree(), relpath
357
361
 
358
362
    @staticmethod
530
534
        return os.path.getsize(self.id2abspath(file_id))
531
535
 
532
536
    @needs_read_lock
533
 
    def get_file_sha1(self, file_id):
534
 
        path = self._inventory.id2path(file_id)
 
537
    def get_file_sha1(self, file_id, path=None):
 
538
        if not path:
 
539
            path = self._inventory.id2path(file_id)
535
540
        return self._hashcache.get_sha1(path)
536
541
 
537
 
    def is_executable(self, file_id):
538
 
        if not supports_executable():
 
542
    def get_file_mtime(self, file_id, path=None):
 
543
        if not path:
 
544
            path = self._inventory.id2path(file_id)
 
545
        return os.lstat(self.abspath(path)).st_mtime
 
546
 
 
547
    if not supports_executable():
 
548
        def is_executable(self, file_id, path=None):
539
549
            return self._inventory[file_id].executable
540
 
        else:
541
 
            path = self._inventory.id2path(file_id)
 
550
    else:
 
551
        def is_executable(self, file_id, path=None):
 
552
            if not path:
 
553
                path = self._inventory.id2path(file_id)
542
554
            mode = os.lstat(self.abspath(path)).st_mode
543
555
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
544
556
 
690
702
            return '?'
691
703
 
692
704
    def list_files(self):
693
 
        """Recursively list all files as (path, class, kind, id).
 
705
        """Recursively list all files as (path, class, kind, id, entry).
694
706
 
695
707
        Lists, but does not descend into unversioned directories.
696
708
 
700
712
        Skips the control directory.
701
713
        """
702
714
        inv = self._inventory
703
 
 
704
 
        def descend(from_dir_relpath, from_dir_id, dp):
705
 
            ls = os.listdir(dp)
706
 
            ls.sort()
707
 
            for f in ls:
 
715
        # Convert these into local objects to save lookup times
 
716
        pathjoin = bzrlib.osutils.pathjoin
 
717
        file_kind = bzrlib.osutils.file_kind
 
718
 
 
719
        # transport.base ends in a slash, we want the piece
 
720
        # between the last two slashes
 
721
        transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
 
722
 
 
723
        fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
 
724
 
 
725
        # directory file_id, relative path, absolute path, reverse sorted children
 
726
        children = os.listdir(self.basedir)
 
727
        children.sort()
 
728
        # jam 20060527 The kernel sized tree seems equivalent whether we 
 
729
        # use a deque and popleft to keep them sorted, or if we use a plain
 
730
        # list and just reverse() them.
 
731
        children = collections.deque(children)
 
732
        stack = [(inv.root.file_id, u'', self.basedir, children)]
 
733
        while stack:
 
734
            from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
 
735
 
 
736
            while children:
 
737
                f = children.popleft()
708
738
                ## TODO: If we find a subdirectory with its own .bzr
709
739
                ## directory, then that is a separate tree and we
710
740
                ## should exclude it.
711
741
 
712
742
                # the bzrdir for this tree
713
 
                if self.bzrdir.transport.base.endswith(f + '/'):
 
743
                if transport_base_dir == f:
714
744
                    continue
715
745
 
716
 
                # path within tree
717
 
                fp = appendpath(from_dir_relpath, f)
 
746
                # we know that from_dir_relpath and from_dir_abspath never end in a slash
 
747
                # and 'f' doesn't begin with one, we can do a string op, rather
 
748
                # than the checks of pathjoin(), all relative paths will have an extra slash
 
749
                # at the beginning
 
750
                fp = from_dir_relpath + '/' + f
718
751
 
719
752
                # absolute path
720
 
                fap = appendpath(dp, f)
 
753
                fap = from_dir_abspath + '/' + f
721
754
                
722
755
                f_ie = inv.get_child(from_dir_id, f)
723
756
                if f_ie:
724
757
                    c = 'V'
725
 
                elif self.is_ignored(fp):
 
758
                elif self.is_ignored(fp[1:]):
726
759
                    c = 'I'
727
760
                else:
728
761
                    c = '?'
737
770
 
738
771
                # make a last minute entry
739
772
                if f_ie:
740
 
                    entry = f_ie
 
773
                    yield fp[1:], c, fk, f_ie.file_id, f_ie
741
774
                else:
742
 
                    if fk == 'directory':
743
 
                        entry = TreeDirectory()
744
 
                    elif fk == 'file':
745
 
                        entry = TreeFile()
746
 
                    elif fk == 'symlink':
747
 
                        entry = TreeLink()
748
 
                    else:
749
 
                        entry = TreeEntry()
 
775
                    try:
 
776
                        yield fp[1:], c, fk, None, fk_entries[fk]()
 
777
                    except KeyError:
 
778
                        yield fp[1:], c, fk, None, TreeEntry()
 
779
                    continue
750
780
                
751
 
                yield fp, c, fk, (f_ie and f_ie.file_id), entry
752
 
 
753
781
                if fk != 'directory':
754
782
                    continue
755
783
 
756
 
                if c != 'V':
757
 
                    # don't descend unversioned directories
758
 
                    continue
759
 
                
760
 
                for ff in descend(fp, f_ie.file_id, fap):
761
 
                    yield ff
 
784
                # But do this child first
 
785
                new_children = os.listdir(fap)
 
786
                new_children.sort()
 
787
                new_children = collections.deque(new_children)
 
788
                stack.append((f_ie.file_id, fp, fap, new_children))
 
789
                # Break out of inner loop, so that we start outer loop with child
 
790
                break
 
791
            else:
 
792
                # if we finished all children, pop it off the stack
 
793
                stack.pop()
762
794
 
763
 
        for f in descend(u'', inv.root.file_id, self.basedir):
764
 
            yield f
765
795
 
766
796
    @needs_write_lock
767
797
    def move(self, from_paths, to_name):
803
833
            if f_id == None:
804
834
                raise BzrError("%r is not versioned" % f)
805
835
            name_tail = splitpath(f)[-1]
806
 
            dest_path = appendpath(to_name, name_tail)
 
836
            dest_path = pathjoin(to_name, name_tail)
807
837
            if self.has_filename(dest_path):
808
838
                raise BzrError("destination %r already exists" % dest_path)
809
839
            if f_id in to_idpath:
816
846
        try:
817
847
            for f in from_paths:
818
848
                name_tail = splitpath(f)[-1]
819
 
                dest_path = appendpath(to_name, name_tail)
 
849
                dest_path = pathjoin(to_name, name_tail)
820
850
                result.append((f, dest_path))
821
851
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
822
852
                try:
912
942
 
913
943
    def _iter_conflicts(self):
914
944
        conflicted = set()
915
 
        for path in (s[0] for s in self.list_files()):
 
945
        for info in self.list_files():
 
946
            path = info[0]
916
947
            stem = get_conflicted_stem(path)
917
948
            if stem is None:
918
949
                continue
978
1009
            
979
1010
            fl.sort()
980
1011
            for subf in fl:
981
 
                subp = appendpath(path, subf)
 
1012
                subp = pathjoin(path, subf)
982
1013
                yield subp
983
1014
 
984
1015
    def _translate_ignore_rule(self, rule):
1054
1085
        l = bzrlib.DEFAULT_IGNORE[:]
1055
1086
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1056
1087
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1057
 
            l.extend([line.rstrip("\n\r") for line in f.readlines()])
 
1088
            l.extend([line.rstrip("\n\r").decode('utf-8') 
 
1089
                      for line in f.readlines()])
1058
1090
        self._ignorelist = l
1059
1091
        self._ignore_regex = self._combine_ignore_rules(l)
1060
1092
        return l
1166
1198
 
1167
1199
    def _cache_basis_inventory(self, new_revision):
1168
1200
        """Cache new_revision as the basis inventory."""
 
1201
        # TODO: this should allow the ready-to-use inventory to be passed in,
 
1202
        # as commit already has that ready-to-use [while the format is the
 
1203
        # same, that is].
1169
1204
        try:
1170
1205
            # this double handles the inventory - unpack and repack - 
1171
1206
            # but is easier to understand. We can/should put a conditional
1172
1207
            # in here based on whether the inventory is in the latest format
1173
1208
            # - perhaps we should repack all inventories on a repository
1174
1209
            # upgrade ?
1175
 
            inv = self.branch.repository.get_inventory(new_revision)
1176
 
            inv.revision_id = new_revision
1177
 
            xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1210
            # the fast path is to copy the raw xml from the repository. If the
 
1211
            # xml contains 'revision_id="', then we assume the right 
 
1212
            # revision_id is set. We must check for this full string, because a
 
1213
            # root node id can legitimately look like 'revision_id' but cannot
 
1214
            # contain a '"'.
 
1215
            xml = self.branch.repository.get_inventory_xml(new_revision)
 
1216
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1217
                inv = self.branch.repository.deserialise_inventory(
 
1218
                    new_revision, xml)
 
1219
                inv.revision_id = new_revision
 
1220
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1178
1221
 
1179
1222
            path = self._basis_inventory_name()
1180
1223
            self._control_files.put_utf8(path, xml)
1197
1240
        return result
1198
1241
 
1199
1242
    @needs_write_lock
1200
 
    def remove(self, files, verbose=False):
 
1243
    def remove(self, files, verbose=False, to_file=None):
1201
1244
        """Remove nominated files from the working inventory..
1202
1245
 
1203
1246
        This does not remove their text.  This does not run on XXX on what? RBC
1232
1275
                    new_status = 'I'
1233
1276
                else:
1234
1277
                    new_status = '?'
1235
 
                show_status(new_status, inv[fid].kind, quotefn(f))
 
1278
                show_status(new_status, inv[fid].kind, quotefn(f), to_file=to_file)
1236
1279
            del inv[fid]
1237
1280
 
1238
1281
        self._write_inventory(inv)
1615
1658
                branch.unlock()
1616
1659
        revision = branch.last_revision()
1617
1660
        inv = Inventory() 
1618
 
        wt = WorkingTree(a_bzrdir.root_transport.base,
 
1661
        wt = WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1619
1662
                         branch,
1620
1663
                         inv,
1621
1664
                         _internal=True,
1643
1686
            raise NotImplementedError
1644
1687
        if not isinstance(a_bzrdir.transport, LocalTransport):
1645
1688
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1646
 
        return WorkingTree(a_bzrdir.root_transport.base,
 
1689
        return WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1647
1690
                           _internal=True,
1648
1691
                           _format=self,
1649
1692
                           _bzrdir=a_bzrdir)
1694
1737
        if revision_id is None:
1695
1738
            revision_id = branch.last_revision()
1696
1739
        inv = Inventory() 
1697
 
        wt = WorkingTree3(a_bzrdir.root_transport.base,
 
1740
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1698
1741
                         branch,
1699
1742
                         inv,
1700
1743
                         _internal=True,
1729
1772
        if not isinstance(a_bzrdir.transport, LocalTransport):
1730
1773
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1731
1774
        control_files = self._open_control_files(a_bzrdir)
1732
 
        return WorkingTree3(a_bzrdir.root_transport.base,
 
1775
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1733
1776
                           _internal=True,
1734
1777
                           _format=self,
1735
1778
                           _bzrdir=a_bzrdir,