/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: James Blackwell
  • Date: 2006-05-05 01:05:04 UTC
  • mfrom: (1697 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1700.
  • Revision ID: jblack@merconline.com-20060505010504-264cb48507e53b64
mergedĀ mainline

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
"""
31
31
 
32
32
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
 
33
CONFLICT_HEADER_1 = "BZR conflict list format 1"
33
34
 
34
35
# TODO: Give the workingtree sole responsibility for the working inventory;
35
36
# remove the variable and references to it from the branch.  This may require
49
50
from bzrlib.atomicfile import AtomicFile
50
51
from bzrlib.branch import (Branch,
51
52
                           quotefn)
 
53
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
52
54
import bzrlib.bzrdir as bzrdir
53
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
56
import bzrlib.errors as errors
55
57
from bzrlib.errors import (BzrCheckError,
56
58
                           BzrError,
 
59
                           ConflictFormatError,
57
60
                           DivergedBranches,
58
61
                           WeaveRevisionNotPresent,
59
62
                           NotBranchError,
60
63
                           NoSuchFile,
61
64
                           NotVersionedError,
62
 
                           MergeModifiedFormatError)
 
65
                           MergeModifiedFormatError,
 
66
                           UnsupportedOperation,
 
67
                           )
63
68
from bzrlib.inventory import InventoryEntry, Inventory
64
69
from bzrlib.lockable_files import LockableFiles, TransportLock
65
70
from bzrlib.lockdir import LockDir
84
89
                            )
85
90
from bzrlib.progress import DummyProgress, ProgressPhase
86
91
from bzrlib.revision import NULL_REVISION
87
 
from bzrlib.rio import RioReader, RioWriter, Stanza
 
92
from bzrlib.rio import RioReader, rio_file, Stanza
88
93
from bzrlib.symbol_versioning import *
89
94
from bzrlib.textui import show_status
90
95
import bzrlib.tree
91
 
from bzrlib.trace import mutter
92
96
from bzrlib.transform import build_tree
 
97
from bzrlib.trace import mutter, note
93
98
from bzrlib.transport import get_transport
94
99
from bzrlib.transport.local import LocalTransport
95
100
import bzrlib.ui
218
223
                 DeprecationWarning,
219
224
                 stacklevel=2)
220
225
            wt = WorkingTree.open(basedir)
221
 
            self.branch = wt.branch
 
226
            self._branch = wt.branch
222
227
            self.basedir = wt.basedir
223
228
            self._control_files = wt._control_files
224
229
            self._hashcache = wt._hashcache
234
239
        if deprecated_passed(branch):
235
240
            if not _internal:
236
241
                warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
237
 
                     " Please use bzrdir.open_workingtree() or WorkingTree.open().",
 
242
                     " Please use bzrdir.open_workingtree() or"
 
243
                     " WorkingTree.open().",
238
244
                     DeprecationWarning,
239
245
                     stacklevel=2
240
246
                     )
241
 
            self.branch = branch
 
247
            self._branch = branch
242
248
        else:
243
 
            self.branch = self.bzrdir.open_branch()
 
249
            self._branch = self.bzrdir.open_branch()
244
250
        assert isinstance(self.branch, Branch), \
245
251
            "branch %r is not a Branch" % self.branch
246
252
        self.basedir = realpath(basedir)
276
282
        else:
277
283
            self._set_inventory(_inventory)
278
284
 
 
285
    branch = property(
 
286
        fget=lambda self: self._branch,
 
287
        doc="""The branch this WorkingTree is connected to.
 
288
 
 
289
            This cannot be set - it is reflective of the actual disk structure
 
290
            the working tree has been constructed from.
 
291
            """)
 
292
 
279
293
    def _set_inventory(self, inv):
280
294
        self._inventory = inv
281
295
        self.path2id = self._inventory.path2id
350
364
        revision_id = self.last_revision()
351
365
        if revision_id is not None:
352
366
            try:
353
 
                xml = self.read_basis_inventory(revision_id)
 
367
                xml = self.read_basis_inventory()
354
368
                inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
369
            except NoSuchFile:
 
370
                inv = None
 
371
            if inv is not None and inv.revision_id == revision_id:
355
372
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
356
373
                                                revision_id)
357
 
            except NoSuchFile:
358
 
                pass
 
374
        # FIXME? RBC 20060403 should we cache the inventory here ?
359
375
        return self.branch.repository.revision_tree(revision_id)
360
376
 
361
377
    @staticmethod
605
621
 
606
622
    @needs_write_lock
607
623
    def set_merge_modified(self, modified_hashes):
608
 
        my_file = StringIO()
609
 
        my_file.write(MERGE_MODIFIED_HEADER_1 + '\n')
610
 
        writer = RioWriter(my_file)
611
 
        for file_id, hash in modified_hashes.iteritems():
612
 
            s = Stanza(file_id=file_id, hash=hash)
613
 
            writer.write_stanza(s)
614
 
        my_file.seek(0)
615
 
        self._control_files.put('merge-hashes', my_file)
 
624
        def iter_stanzas():
 
625
            for file_id, hash in modified_hashes.iteritems():
 
626
                yield Stanza(file_id=file_id, hash=hash)
 
627
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
628
 
 
629
    @needs_write_lock
 
630
    def _put_rio(self, filename, stanzas, header):
 
631
        my_file = rio_file(stanzas, header)
 
632
        self._control_files.put(filename, my_file)
616
633
 
617
634
    @needs_read_lock
618
635
    def merge_modified(self):
628
645
            raise MergeModifiedFormatError()
629
646
        for s in RioReader(hashfile):
630
647
            file_id = s.get("file_id")
 
648
            if file_id not in self.inventory:
 
649
                continue
631
650
            hash = s.get("hash")
632
651
            if hash == self.get_file_sha1(file_id):
633
652
                merge_hashes[file_id] = hash
859
878
            if not self.is_ignored(subp):
860
879
                yield subp
861
880
 
 
881
    @deprecated_method(zero_eight)
862
882
    def iter_conflicts(self):
 
883
        """List all files in the tree that have text or content conflicts.
 
884
        DEPRECATED.  Use conflicts instead."""
 
885
        return self._iter_conflicts()
 
886
 
 
887
    def _iter_conflicts(self):
863
888
        conflicted = set()
864
889
        for path in (s[0] for s in self.list_files()):
865
890
            stem = get_conflicted_stem(path)
1006
1031
        """
1007
1032
        return self.branch.last_revision()
1008
1033
 
 
1034
    def is_locked(self):
 
1035
        return self._control_files.is_locked()
 
1036
 
1009
1037
    def lock_read(self):
1010
1038
        """See Branch.lock_read, and WorkingTree.unlock."""
1011
1039
        self.branch.lock_read()
1024
1052
            self.branch.unlock()
1025
1053
            raise
1026
1054
 
1027
 
    def _basis_inventory_name(self, revision_id):
1028
 
        return 'basis-inventory.%s' % revision_id
 
1055
    def get_physical_lock_status(self):
 
1056
        return self._control_files.get_physical_lock_status()
 
1057
 
 
1058
    def _basis_inventory_name(self):
 
1059
        return 'basis-inventory'
1029
1060
 
1030
1061
    @needs_write_lock
1031
 
    def set_last_revision(self, new_revision, old_revision=None):
 
1062
    def set_last_revision(self, new_revision):
1032
1063
        """Change the last revision in the working tree."""
1033
 
        self._remove_old_basis(old_revision)
1034
1064
        if self._change_last_revision(new_revision):
1035
1065
            self._cache_basis_inventory(new_revision)
1036
1066
 
1037
1067
    def _change_last_revision(self, new_revision):
1038
 
        """Template method part of set_last_revision to perform the change."""
 
1068
        """Template method part of set_last_revision to perform the change.
 
1069
        
 
1070
        This is used to allow WorkingTree3 instances to not affect branch
 
1071
        when their last revision is set.
 
1072
        """
1039
1073
        if new_revision is None:
1040
1074
            self.branch.set_revision_history([])
1041
1075
            return False
1051
1085
    def _cache_basis_inventory(self, new_revision):
1052
1086
        """Cache new_revision as the basis inventory."""
1053
1087
        try:
1054
 
            xml = self.branch.repository.get_inventory_xml(new_revision)
1055
 
            path = self._basis_inventory_name(new_revision)
 
1088
            # this double handles the inventory - unpack and repack - 
 
1089
            # but is easier to understand. We can/should put a conditional
 
1090
            # in here based on whether the inventory is in the latest format
 
1091
            # - perhaps we should repack all inventories on a repository
 
1092
            # upgrade ?
 
1093
            inv = self.branch.repository.get_inventory(new_revision)
 
1094
            inv.revision_id = new_revision
 
1095
            xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1096
 
 
1097
            path = self._basis_inventory_name()
1056
1098
            self._control_files.put_utf8(path, xml)
1057
1099
        except WeaveRevisionNotPresent:
1058
1100
            pass
1059
1101
 
1060
 
    def _remove_old_basis(self, old_revision):
1061
 
        """Remove the old basis inventory 'old_revision'."""
1062
 
        if old_revision is not None:
1063
 
            try:
1064
 
                path = self._basis_inventory_name(old_revision)
1065
 
                path = self._control_files._escape(path)
1066
 
                self._control_files._transport.delete(path)
1067
 
            except NoSuchFile:
1068
 
                pass
1069
 
 
1070
 
    def read_basis_inventory(self, revision_id):
 
1102
    def read_basis_inventory(self):
1071
1103
        """Read the cached basis inventory."""
1072
 
        path = self._basis_inventory_name(revision_id)
 
1104
        path = self._basis_inventory_name()
1073
1105
        return self._control_files.get_utf8(path).read()
1074
1106
        
1075
1107
    @needs_read_lock
1127
1159
    def revert(self, filenames, old_tree=None, backups=True, 
1128
1160
               pb=DummyProgress()):
1129
1161
        from transform import revert
 
1162
        from conflicts import resolve
1130
1163
        if old_tree is None:
1131
1164
            old_tree = self.basis_tree()
1132
 
        revert(self, old_tree, filenames, backups, pb)
 
1165
        conflicts = revert(self, old_tree, filenames, backups, pb)
1133
1166
        if not len(filenames):
1134
1167
            self.set_pending_merges([])
 
1168
            resolve(self)
 
1169
        else:
 
1170
            resolve(self, filenames, ignore_misses=True)
 
1171
        return conflicts
1135
1172
 
 
1173
    # XXX: This method should be deprecated in favour of taking in a proper
 
1174
    # new Inventory object.
1136
1175
    @needs_write_lock
1137
1176
    def set_inventory(self, new_inventory_list):
1138
1177
        from bzrlib.inventory import (Inventory,
1269
1308
        self._set_inventory(inv)
1270
1309
        mutter('wrote working inventory')
1271
1310
 
 
1311
    def set_conflicts(self, arg):
 
1312
        raise UnsupportedOperation(self.set_conflicts, self)
 
1313
 
 
1314
    @needs_read_lock
 
1315
    def conflicts(self):
 
1316
        conflicts = ConflictList()
 
1317
        for conflicted in self._iter_conflicts():
 
1318
            text = True
 
1319
            try:
 
1320
                if file_kind(self.abspath(conflicted)) != "file":
 
1321
                    text = False
 
1322
            except OSError, e:
 
1323
                if e.errno == errno.ENOENT:
 
1324
                    text = False
 
1325
                else:
 
1326
                    raise
 
1327
            if text is True:
 
1328
                for suffix in ('.THIS', '.OTHER'):
 
1329
                    try:
 
1330
                        kind = file_kind(self.abspath(conflicted+suffix))
 
1331
                    except OSError, e:
 
1332
                        if e.errno == errno.ENOENT:
 
1333
                            text = False
 
1334
                            break
 
1335
                        else:
 
1336
                            raise
 
1337
                    if kind != "file":
 
1338
                        text = False
 
1339
                        break
 
1340
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
 
1341
            conflicts.append(Conflict.factory(ctype, path=conflicted,
 
1342
                             file_id=self.path2id(conflicted)))
 
1343
        return conflicts
 
1344
 
1272
1345
 
1273
1346
class WorkingTree3(WorkingTree):
1274
1347
    """This is the Format 3 working tree.
1304
1377
            self._control_files.put_utf8('last-revision', revision_id)
1305
1378
            return True
1306
1379
 
1307
 
 
1308
 
CONFLICT_SUFFIXES = ('.THIS', '.BASE', '.OTHER')
 
1380
    @needs_write_lock
 
1381
    def set_conflicts(self, conflicts):
 
1382
        self._put_rio('conflicts', conflicts.to_stanzas(), 
 
1383
                      CONFLICT_HEADER_1)
 
1384
 
 
1385
    @needs_read_lock
 
1386
    def conflicts(self):
 
1387
        try:
 
1388
            confile = self._control_files.get('conflicts')
 
1389
        except NoSuchFile:
 
1390
            return ConflictList()
 
1391
        try:
 
1392
            if confile.next() != CONFLICT_HEADER_1 + '\n':
 
1393
                raise ConflictFormatError()
 
1394
        except StopIteration:
 
1395
            raise ConflictFormatError()
 
1396
        return ConflictList.from_stanzas(RioReader(confile))
 
1397
 
 
1398
 
1309
1399
def get_conflicted_stem(path):
1310
1400
    for suffix in CONFLICT_SUFFIXES:
1311
1401
        if path.endswith(suffix):
1372
1462
        """Return the ASCII format string that identifies this format."""
1373
1463
        raise NotImplementedError(self.get_format_string)
1374
1464
 
 
1465
    def get_format_description(self):
 
1466
        """Return the short description for this format."""
 
1467
        raise NotImplementedError(self.get_format_description)
 
1468
 
1375
1469
    def is_supported(self):
1376
1470
        """Is this format supported?
1377
1471
 
1402
1496
    This format modified the hash cache from the format 1 hash cache.
1403
1497
    """
1404
1498
 
 
1499
    def get_format_description(self):
 
1500
        """See WorkingTreeFormat.get_format_description()."""
 
1501
        return "Working tree format 2"
 
1502
 
1405
1503
    def initialize(self, a_bzrdir, revision_id=None):
1406
1504
        """See WorkingTreeFormat.initialize()."""
1407
1505
        if not isinstance(a_bzrdir.transport, LocalTransport):
1470
1568
        """See WorkingTreeFormat.get_format_string()."""
1471
1569
        return "Bazaar-NG Working Tree format 3"
1472
1570
 
 
1571
    def get_format_description(self):
 
1572
        """See WorkingTreeFormat.get_format_description()."""
 
1573
        return "Working tree format 3"
 
1574
 
1473
1575
    _lock_file_name = 'lock'
1474
1576
    _lock_class = LockDir
1475
1577
 
1489
1591
        transport = a_bzrdir.get_workingtree_transport(self)
1490
1592
        control_files = self._open_control_files(a_bzrdir)
1491
1593
        control_files.create_lock()
 
1594
        control_files.lock_write()
1492
1595
        control_files.put_utf8('format', self.get_format_string())
1493
1596
        branch = a_bzrdir.open_branch()
1494
1597
        if revision_id is None:
1501
1604
                         _format=self,
1502
1605
                         _bzrdir=a_bzrdir,
1503
1606
                         _control_files=control_files)
1504
 
        wt._write_inventory(inv)
1505
 
        wt.set_root_id(inv.root.file_id)
1506
 
        wt.set_last_revision(revision_id)
1507
 
        wt.set_pending_merges([])
1508
 
        build_tree(wt.basis_tree(), wt)
 
1607
        wt.lock_write()
 
1608
        try:
 
1609
            wt._write_inventory(inv)
 
1610
            wt.set_root_id(inv.root.file_id)
 
1611
            wt.set_last_revision(revision_id)
 
1612
            wt.set_pending_merges([])
 
1613
            build_tree(wt.basis_tree(), wt)
 
1614
        finally:
 
1615
            wt.unlock()
 
1616
            control_files.unlock()
1509
1617
        return wt
1510
1618
 
1511
1619
    def __init__(self):