/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 bzr.dev, fix minor conflict in cmd_revision_history().

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
 
148
149
 
149
150
 
150
151
class TreeEntry(object):
151
 
    """An entry that implements the minium interface used by commands.
 
152
    """An entry that implements the minimum interface used by commands.
152
153
 
153
154
    This needs further inspection, it may be better to have 
154
155
    InventoryEntries without ids - though that seems wrong. For now,
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
 
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
 
555
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
544
556
 
545
557
    @needs_write_lock
546
558
    def add(self, files, ids=None):
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)
1178
 
 
 
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)
 
1221
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1179
1222
            path = self._basis_inventory_name()
1180
 
            self._control_files.put_utf8(path, xml)
 
1223
            sio = StringIO(xml)
 
1224
            self._control_files.put(path, sio)
1181
1225
        except WeaveRevisionNotPresent:
1182
1226
            pass
1183
1227
 
1184
1228
    def read_basis_inventory(self):
1185
1229
        """Read the cached basis inventory."""
1186
1230
        path = self._basis_inventory_name()
1187
 
        return self._control_files.get_utf8(path).read()
 
1231
        return self._control_files.get(path).read()
1188
1232
        
1189
1233
    @needs_read_lock
1190
1234
    def read_working_inventory(self):
1197
1241
        return result
1198
1242
 
1199
1243
    @needs_write_lock
1200
 
    def remove(self, files, verbose=False):
 
1244
    def remove(self, files, verbose=False, to_file=None):
1201
1245
        """Remove nominated files from the working inventory..
1202
1246
 
1203
1247
        This does not remove their text.  This does not run on XXX on what? RBC
1232
1276
                    new_status = 'I'
1233
1277
                else:
1234
1278
                    new_status = '?'
1235
 
                show_status(new_status, inv[fid].kind, quotefn(f))
 
1279
                show_status(new_status, inv[fid].kind, quotefn(f), to_file=to_file)
1236
1280
            del inv[fid]
1237
1281
 
1238
1282
        self._write_inventory(inv)
1305
1349
        # of a nasty hack; probably it's better to have a transaction object,
1306
1350
        # which can do some finalization when it's either successfully or
1307
1351
        # unsuccessfully completed.  (Denys's original patch did that.)
1308
 
        # RBC 20060206 hookinhg into transaction will couple lock and transaction
1309
 
        # wrongly. Hookinh into unllock on the control files object is fine though.
 
1352
        # RBC 20060206 hooking into transaction will couple lock and transaction
 
1353
        # wrongly. Hooking into unlock on the control files object is fine though.
1310
1354
        
1311
1355
        # TODO: split this per format so there is no ugly if block
1312
1356
        if self._hashcache.needs_write and (
1358
1402
                                      this_tree=self)
1359
1403
                self.set_last_revision(self.branch.last_revision())
1360
1404
            if old_tip and old_tip != self.last_revision():
1361
 
                # our last revision was not the prior branch last reivison
 
1405
                # our last revision was not the prior branch last revision
1362
1406
                # and we have converted that last revision to a pending merge.
1363
1407
                # base is somewhere between the branch tip now
1364
1408
                # and the now pending merge
1400
1444
            try:
1401
1445
                if file_kind(self.abspath(conflicted)) != "file":
1402
1446
                    text = False
1403
 
            except OSError, e:
1404
 
                if e.errno == errno.ENOENT:
1405
 
                    text = False
1406
 
                else:
1407
 
                    raise
 
1447
            except errors.NoSuchFile:
 
1448
                text = False
1408
1449
            if text is True:
1409
1450
                for suffix in ('.THIS', '.OTHER'):
1410
1451
                    try:
1411
1452
                        kind = file_kind(self.abspath(conflicted+suffix))
1412
 
                    except OSError, e:
1413
 
                        if e.errno == errno.ENOENT:
 
1453
                        if kind != "file":
1414
1454
                            text = False
1415
 
                            break
1416
 
                        else:
1417
 
                            raise
1418
 
                    if kind != "file":
 
1455
                    except errors.NoSuchFile:
1419
1456
                        text = False
 
1457
                    if text == False:
1420
1458
                        break
1421
1459
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1422
1460
            conflicts.append(Conflict.factory(ctype, path=conflicted,
1615
1653
                branch.unlock()
1616
1654
        revision = branch.last_revision()
1617
1655
        inv = Inventory() 
1618
 
        wt = WorkingTree(a_bzrdir.root_transport.base,
 
1656
        wt = WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1619
1657
                         branch,
1620
1658
                         inv,
1621
1659
                         _internal=True,
1643
1681
            raise NotImplementedError
1644
1682
        if not isinstance(a_bzrdir.transport, LocalTransport):
1645
1683
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1646
 
        return WorkingTree(a_bzrdir.root_transport.base,
 
1684
        return WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1647
1685
                           _internal=True,
1648
1686
                           _format=self,
1649
1687
                           _bzrdir=a_bzrdir)
1680
1718
    def initialize(self, a_bzrdir, revision_id=None):
1681
1719
        """See WorkingTreeFormat.initialize().
1682
1720
        
1683
 
        revision_id allows creating a working tree at a differnet
 
1721
        revision_id allows creating a working tree at a different
1684
1722
        revision than the branch is at.
1685
1723
        """
1686
1724
        if not isinstance(a_bzrdir.transport, LocalTransport):
1694
1732
        if revision_id is None:
1695
1733
            revision_id = branch.last_revision()
1696
1734
        inv = Inventory() 
1697
 
        wt = WorkingTree3(a_bzrdir.root_transport.base,
 
1735
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1698
1736
                         branch,
1699
1737
                         inv,
1700
1738
                         _internal=True,
1729
1767
        if not isinstance(a_bzrdir.transport, LocalTransport):
1730
1768
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1731
1769
        control_files = self._open_control_files(a_bzrdir)
1732
 
        return WorkingTree3(a_bzrdir.root_transport.base,
 
1770
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1733
1771
                           _internal=True,
1734
1772
                           _format=self,
1735
1773
                           _bzrdir=a_bzrdir,