~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Pool
  • Date: 2006-06-20 07:55:43 UTC
  • mfrom: (1798 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1799.
  • Revision ID: mbp@sourcefrog.net-20060620075543-b10f6575d4a4fa32
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
49
49
import re
50
50
import stat
51
51
from time import time
 
52
import warnings
52
53
 
53
54
from bzrlib.atomicfile import AtomicFile
54
 
from bzrlib.branch import (Branch,
55
 
                           quotefn)
56
55
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
57
56
import bzrlib.bzrdir as bzrdir
58
57
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
59
from bzrlib.errors import (BzrCheckError,
61
60
                           BzrError,
62
61
                           ConflictFormatError,
63
 
                           DivergedBranches,
64
62
                           WeaveRevisionNotPresent,
65
63
                           NotBranchError,
66
64
                           NoSuchFile,
92
90
from bzrlib.progress import DummyProgress, ProgressPhase
93
91
from bzrlib.revision import NULL_REVISION
94
92
from bzrlib.rio import RioReader, rio_file, Stanza
95
 
from bzrlib.symbol_versioning import *
 
93
from bzrlib.symbol_versioning import (deprecated_passed,
 
94
        deprecated_method,
 
95
        deprecated_function,
 
96
        DEPRECATED_PARAMETER,
 
97
        zero_eight,
 
98
        )
 
99
 
96
100
from bzrlib.textui import show_status
97
101
import bzrlib.tree
98
102
from bzrlib.transform import build_tree
99
103
from bzrlib.trace import mutter, note
100
104
from bzrlib.transport import get_transport
101
105
from bzrlib.transport.local import LocalTransport
 
106
import bzrlib.urlutils as urlutils
102
107
import bzrlib.ui
103
108
import bzrlib.xml5
104
109
 
148
153
 
149
154
 
150
155
class TreeEntry(object):
151
 
    """An entry that implements the minium interface used by commands.
 
156
    """An entry that implements the minimum interface used by commands.
152
157
 
153
158
    This needs further inspection, it may be better to have 
154
159
    InventoryEntries without ids - though that seems wrong. For now,
230
235
        self.bzrdir = _bzrdir
231
236
        if not _internal:
232
237
            # not created via open etc.
233
 
            warn("WorkingTree() is deprecated as of bzr version 0.8. "
 
238
            warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
234
239
                 "Please use bzrdir.open_workingtree or WorkingTree.open().",
235
240
                 DeprecationWarning,
236
241
                 stacklevel=2)
250
255
        mutter("opening working tree %r", basedir)
251
256
        if deprecated_passed(branch):
252
257
            if not _internal:
253
 
                warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
 
258
                warnings.warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
254
259
                     " Please use bzrdir.open_workingtree() or"
255
260
                     " WorkingTree.open().",
256
261
                     DeprecationWarning,
259
264
            self._branch = branch
260
265
        else:
261
266
            self._branch = self.bzrdir.open_branch()
262
 
        assert isinstance(self.branch, Branch), \
263
 
            "branch %r is not a Branch" % self.branch
264
267
        self.basedir = realpath(basedir)
265
268
        # if branch is at our basedir and is a format 6 or less
266
269
        if isinstance(self._format, WorkingTreeFormat2):
279
282
        # if needed, or, when the cache sees a change, append it to the hash
280
283
        # cache file, and have the parser take the most recent entry for a
281
284
        # given path only.
282
 
        cache_filename = self.bzrdir.get_workingtree_transport(None).abspath('stat-cache')
 
285
        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
283
286
        hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
284
287
        hc.read()
285
288
        # is this scan needed ? it makes things kinda slow.
349
352
        run into /.  If there isn't one, raises NotBranchError.
350
353
        TODO: give this a new exception.
351
354
        If there is one, it is returned, along with the unused portion of path.
 
355
 
 
356
        :return: The WorkingTree that contains 'path', and the rest of path
352
357
        """
353
358
        if path is None:
354
359
            path = os.getcwdu()
355
360
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
361
 
356
362
        return control.open_workingtree(), relpath
357
363
 
358
364
    @staticmethod
413
419
        XXX: When BzrDir is present, these should be created through that 
414
420
        interface instead.
415
421
        """
416
 
        warn('delete WorkingTree.create', stacklevel=3)
 
422
        warnings.warn('delete WorkingTree.create', stacklevel=3)
417
423
        transport = get_transport(directory)
418
424
        if branch.bzrdir.root_transport.base == transport.base:
419
425
            # same dir 
451
457
    def get_file_byname(self, filename):
452
458
        return file(self.abspath(filename), 'rb')
453
459
 
 
460
    def get_parent_ids(self):
 
461
        """See Tree.get_parent_ids.
 
462
        
 
463
        This implementation reads the pending merges list and last_revision
 
464
        value and uses that to decide what the parents list should be.
 
465
        """
 
466
        last_rev = self.last_revision()
 
467
        if last_rev is None:
 
468
            parents = []
 
469
        else:
 
470
            parents = [last_rev]
 
471
        other_parents = self.pending_merges()
 
472
        return parents + other_parents
 
473
 
454
474
    def get_root_id(self):
455
475
        """Return the id of this trees root"""
456
476
        inv = self.read_working_inventory()
505
525
        # but with branch a kwarg now, passing in args as is results in the
506
526
        #message being used for the branch
507
527
        args = (DEPRECATED_PARAMETER, message, ) + args
508
 
        Commit().commit(working_tree=self, revprops=revprops, *args, **kwargs)
 
528
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
 
529
            *args, **kwargs)
509
530
        self._set_inventory(self.read_working_inventory())
 
531
        return committed_id
510
532
 
511
533
    def id2abspath(self, file_id):
512
534
        return self.abspath(self.id2path(file_id))
535
557
            path = self._inventory.id2path(file_id)
536
558
        return self._hashcache.get_sha1(path)
537
559
 
 
560
    def get_file_mtime(self, file_id, path=None):
 
561
        if not path:
 
562
            path = self._inventory.id2path(file_id)
 
563
        return os.lstat(self.abspath(path)).st_mtime
 
564
 
538
565
    if not supports_executable():
539
566
        def is_executable(self, file_id, path=None):
540
567
            return self._inventory[file_id].executable
543
570
            if not path:
544
571
                path = self._inventory.id2path(file_id)
545
572
            mode = os.lstat(self.abspath(path)).st_mode
546
 
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
 
573
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
547
574
 
548
575
    @needs_write_lock
549
576
    def add(self, files, ids=None):
584
611
        inv = self.read_working_inventory()
585
612
        for f,file_id in zip(files, ids):
586
613
            if self.is_control_filename(f):
587
 
                raise BzrError("cannot add control file %s" % quotefn(f))
 
614
                raise errors.ForbiddenControlFileError(filename=f)
588
615
 
589
616
            fp = splitpath(f)
590
617
 
592
619
                raise BzrError("cannot add top-level %r" % f)
593
620
 
594
621
            fullpath = normpath(self.abspath(f))
595
 
 
596
622
            try:
597
623
                kind = file_kind(fullpath)
598
624
            except OSError, e:
599
625
                if e.errno == errno.ENOENT:
600
626
                    raise NoSuchFile(fullpath)
601
 
                # maybe something better?
602
 
                raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
603
 
 
604
627
            if not InventoryEntry.versionable_kind(kind):
605
 
                raise BzrError('cannot add: not a versionable file ('
606
 
                               'i.e. regular file, symlink or directory): %s' % quotefn(f))
607
 
 
 
628
                raise errors.BadFileKindError(filename=f, kind=kind)
608
629
            if file_id is None:
609
630
                inv.add_path(f, kind=kind)
610
631
            else:
1063
1084
        l = bzrlib.DEFAULT_IGNORE[:]
1064
1085
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1065
1086
            f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1066
 
            l.extend([line.rstrip("\n\r") for line in f.readlines()])
 
1087
            l.extend([line.rstrip("\n\r").decode('utf-8') 
 
1088
                      for line in f.readlines()])
1067
1089
        self._ignorelist = l
1068
1090
        self._ignore_regex = self._combine_ignore_rules(l)
1069
1091
        return l
1175
1197
 
1176
1198
    def _cache_basis_inventory(self, new_revision):
1177
1199
        """Cache new_revision as the basis inventory."""
 
1200
        # TODO: this should allow the ready-to-use inventory to be passed in,
 
1201
        # as commit already has that ready-to-use [while the format is the
 
1202
        # same, that is].
1178
1203
        try:
1179
1204
            # this double handles the inventory - unpack and repack - 
1180
1205
            # but is easier to understand. We can/should put a conditional
1181
1206
            # in here based on whether the inventory is in the latest format
1182
1207
            # - perhaps we should repack all inventories on a repository
1183
1208
            # upgrade ?
1184
 
            inv = self.branch.repository.get_inventory(new_revision)
1185
 
            inv.revision_id = new_revision
1186
 
            xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1187
 
 
 
1209
            # the fast path is to copy the raw xml from the repository. If the
 
1210
            # xml contains 'revision_id="', then we assume the right 
 
1211
            # revision_id is set. We must check for this full string, because a
 
1212
            # root node id can legitimately look like 'revision_id' but cannot
 
1213
            # contain a '"'.
 
1214
            xml = self.branch.repository.get_inventory_xml(new_revision)
 
1215
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1216
                inv = self.branch.repository.deserialise_inventory(
 
1217
                    new_revision, xml)
 
1218
                inv.revision_id = new_revision
 
1219
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1220
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1188
1221
            path = self._basis_inventory_name()
1189
 
            self._control_files.put_utf8(path, xml)
 
1222
            sio = StringIO(xml)
 
1223
            self._control_files.put(path, sio)
1190
1224
        except WeaveRevisionNotPresent:
1191
1225
            pass
1192
1226
 
1193
1227
    def read_basis_inventory(self):
1194
1228
        """Read the cached basis inventory."""
1195
1229
        path = self._basis_inventory_name()
1196
 
        return self._control_files.get_utf8(path).read()
 
1230
        return self._control_files.get(path).read()
1197
1231
        
1198
1232
    @needs_read_lock
1199
1233
    def read_working_inventory(self):
1206
1240
        return result
1207
1241
 
1208
1242
    @needs_write_lock
1209
 
    def remove(self, files, verbose=False):
 
1243
    def remove(self, files, verbose=False, to_file=None):
1210
1244
        """Remove nominated files from the working inventory..
1211
1245
 
1212
1246
        This does not remove their text.  This does not run on XXX on what? RBC
1234
1268
                # TODO: Perhaps make this just a warning, and continue?
1235
1269
                # This tends to happen when 
1236
1270
                raise NotVersionedError(path=f)
1237
 
            mutter("remove inventory entry %s {%s}", quotefn(f), fid)
1238
1271
            if verbose:
1239
1272
                # having remove it, it must be either ignored or unknown
1240
1273
                if self.is_ignored(f):
1241
1274
                    new_status = 'I'
1242
1275
                else:
1243
1276
                    new_status = '?'
1244
 
                show_status(new_status, inv[fid].kind, quotefn(f))
 
1277
                show_status(new_status, inv[fid].kind, f, to_file=to_file)
1245
1278
            del inv[fid]
1246
1279
 
1247
1280
        self._write_inventory(inv)
1314
1347
        # of a nasty hack; probably it's better to have a transaction object,
1315
1348
        # which can do some finalization when it's either successfully or
1316
1349
        # unsuccessfully completed.  (Denys's original patch did that.)
1317
 
        # RBC 20060206 hookinhg into transaction will couple lock and transaction
1318
 
        # wrongly. Hookinh into unllock on the control files object is fine though.
 
1350
        # RBC 20060206 hooking into transaction will couple lock and transaction
 
1351
        # wrongly. Hooking into unlock on the control files object is fine though.
1319
1352
        
1320
1353
        # TODO: split this per format so there is no ugly if block
1321
1354
        if self._hashcache.needs_write and (
1367
1400
                                      this_tree=self)
1368
1401
                self.set_last_revision(self.branch.last_revision())
1369
1402
            if old_tip and old_tip != self.last_revision():
1370
 
                # our last revision was not the prior branch last reivison
 
1403
                # our last revision was not the prior branch last revision
1371
1404
                # and we have converted that last revision to a pending merge.
1372
1405
                # base is somewhere between the branch tip now
1373
1406
                # and the now pending merge
1409
1442
            try:
1410
1443
                if file_kind(self.abspath(conflicted)) != "file":
1411
1444
                    text = False
1412
 
            except OSError, e:
1413
 
                if e.errno == errno.ENOENT:
1414
 
                    text = False
1415
 
                else:
1416
 
                    raise
 
1445
            except errors.NoSuchFile:
 
1446
                text = False
1417
1447
            if text is True:
1418
1448
                for suffix in ('.THIS', '.OTHER'):
1419
1449
                    try:
1420
1450
                        kind = file_kind(self.abspath(conflicted+suffix))
1421
 
                    except OSError, e:
1422
 
                        if e.errno == errno.ENOENT:
 
1451
                        if kind != "file":
1423
1452
                            text = False
1424
 
                            break
1425
 
                        else:
1426
 
                            raise
1427
 
                    if kind != "file":
 
1453
                    except errors.NoSuchFile:
1428
1454
                        text = False
 
1455
                    if text == False:
1429
1456
                        break
1430
1457
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1431
1458
            conflicts.append(Conflict.factory(ctype, path=conflicted,
1541
1568
        except NoSuchFile:
1542
1569
            raise errors.NoWorkingTree(base=transport.base)
1543
1570
        except KeyError:
1544
 
            raise errors.UnknownFormatError(format_string)
 
1571
            raise errors.UnknownFormatError(format=format_string)
1545
1572
 
1546
1573
    @classmethod
1547
1574
    def get_default_format(klass):
1624
1651
                branch.unlock()
1625
1652
        revision = branch.last_revision()
1626
1653
        inv = Inventory() 
1627
 
        wt = WorkingTree(a_bzrdir.root_transport.base,
 
1654
        wt = WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1628
1655
                         branch,
1629
1656
                         inv,
1630
1657
                         _internal=True,
1652
1679
            raise NotImplementedError
1653
1680
        if not isinstance(a_bzrdir.transport, LocalTransport):
1654
1681
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1655
 
        return WorkingTree(a_bzrdir.root_transport.base,
 
1682
        return WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1656
1683
                           _internal=True,
1657
1684
                           _format=self,
1658
1685
                           _bzrdir=a_bzrdir)
1689
1716
    def initialize(self, a_bzrdir, revision_id=None):
1690
1717
        """See WorkingTreeFormat.initialize().
1691
1718
        
1692
 
        revision_id allows creating a working tree at a differnet
 
1719
        revision_id allows creating a working tree at a different
1693
1720
        revision than the branch is at.
1694
1721
        """
1695
1722
        if not isinstance(a_bzrdir.transport, LocalTransport):
1703
1730
        if revision_id is None:
1704
1731
            revision_id = branch.last_revision()
1705
1732
        inv = Inventory() 
1706
 
        wt = WorkingTree3(a_bzrdir.root_transport.base,
 
1733
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1707
1734
                         branch,
1708
1735
                         inv,
1709
1736
                         _internal=True,
1738
1765
        if not isinstance(a_bzrdir.transport, LocalTransport):
1739
1766
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
1740
1767
        control_files = self._open_control_files(a_bzrdir)
1741
 
        return WorkingTree3(a_bzrdir.root_transport.base,
 
1768
        return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1742
1769
                           _internal=True,
1743
1770
                           _format=self,
1744
1771
                           _bzrdir=a_bzrdir,