/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: John Arbash Meinel
  • Date: 2011-04-07 10:36:24 UTC
  • mfrom: (5764 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5766.
  • Revision ID: john@arbash-meinel.com-20110407103624-n76g6tjeqmznwdcd
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
44
44
import stat
45
45
import re
46
46
 
47
 
import bzrlib
48
47
from bzrlib import (
49
48
    branch,
50
49
    bzrdir,
51
50
    conflicts as _mod_conflicts,
 
51
    controldir,
52
52
    errors,
53
53
    generate_ids,
54
54
    globbing,
59
59
    merge,
60
60
    revision as _mod_revision,
61
61
    revisiontree,
62
 
    trace,
 
62
    rio as _mod_rio,
63
63
    transform,
 
64
    transport,
64
65
    ui,
65
66
    views,
66
67
    xml5,
67
68
    xml7,
68
69
    )
69
 
import bzrlib.branch
70
 
from bzrlib.transport import get_transport
71
70
from bzrlib.workingtree_4 import (
72
71
    WorkingTreeFormat4,
73
72
    WorkingTreeFormat5,
97
96
from bzrlib.trace import mutter, note
98
97
from bzrlib.transport.local import LocalTransport
99
98
from bzrlib.revision import CURRENT_REVISION
100
 
from bzrlib.rio import RioReader, rio_file, Stanza
101
99
from bzrlib.symbol_versioning import (
102
100
    deprecated_passed,
103
101
    DEPRECATED_PARAMETER,
169
167
 
170
168
 
171
169
class WorkingTree(bzrlib.mutabletree.MutableTree,
172
 
    bzrdir.ControlComponent):
 
170
    controldir.ControlComponent):
173
171
    """Working copy tree.
174
172
 
175
173
    The inventory is held in the `Branch` working-inventory, and the
177
175
 
178
176
    It is possible for a `WorkingTree` to have a filename which is
179
177
    not listed in the Inventory and vice versa.
 
178
 
 
179
    :ivar basedir: The root of the tree on disk. This is a unicode path object
 
180
        (as opposed to a URL).
180
181
    """
181
182
 
182
183
    # override this to set the strategy for storing views
206
207
        else:
207
208
            self._branch = self.bzrdir.open_branch()
208
209
        self.basedir = realpath(basedir)
209
 
        # if branch is at our basedir and is a format 6 or less
210
 
        if isinstance(self._format, WorkingTreeFormat2):
211
 
            # share control object
212
 
            self._control_files = self.branch.control_files
213
 
        else:
214
 
            # assume all other formats have their own control files.
215
 
            self._control_files = _control_files
 
210
        self._control_files = _control_files
216
211
        self._transport = self._control_files._transport
217
212
        # update the whole cache up front and write to disk if anything changed;
218
213
        # in the future we might want to do this more selectively
256
251
    def control_transport(self):
257
252
        return self._transport
258
253
 
 
254
    def is_control_filename(self, filename):
 
255
        """True if filename is the name of a control file in this tree.
 
256
 
 
257
        :param filename: A filename within the tree. This is a relative path
 
258
        from the root of this tree.
 
259
 
 
260
        This is true IF and ONLY IF the filename is part of the meta data
 
261
        that bzr controls in this tree. I.E. a random .bzr directory placed
 
262
        on disk will not be a control file for this tree.
 
263
        """
 
264
        return self.bzrdir.is_control_filename(filename)
 
265
 
259
266
    def _detect_case_handling(self):
260
267
        wt_trans = self.bzrdir.get_workingtree_transport(None)
261
268
        try:
262
 
            wt_trans.stat("FoRMaT")
 
269
            wt_trans.stat(self._format.case_sensitive_filename)
263
270
        except errors.NoSuchFile:
264
271
            self.case_sensitive = True
265
272
        else:
347
354
        if path is None:
348
355
            path = osutils.getcwd()
349
356
        control, relpath = bzrdir.BzrDir.open_containing(path)
350
 
 
351
357
        return control.open_workingtree(), relpath
352
358
 
353
359
    @staticmethod
 
360
    def open_containing_paths(file_list, default_directory=None,
 
361
                              canonicalize=True, apply_view=True):
 
362
        """Open the WorkingTree that contains a set of paths.
 
363
 
 
364
        Fail if the paths given are not all in a single tree.
 
365
 
 
366
        This is used for the many command-line interfaces that take a list of
 
367
        any number of files and that require they all be in the same tree.
 
368
        """
 
369
        if default_directory is None:
 
370
            default_directory = u'.'
 
371
        # recommended replacement for builtins.internal_tree_files
 
372
        if file_list is None or len(file_list) == 0:
 
373
            tree = WorkingTree.open_containing(default_directory)[0]
 
374
            # XXX: doesn't really belong here, and seems to have the strange
 
375
            # side effect of making it return a bunch of files, not the whole
 
376
            # tree -- mbp 20100716
 
377
            if tree.supports_views() and apply_view:
 
378
                view_files = tree.views.lookup_view()
 
379
                if view_files:
 
380
                    file_list = view_files
 
381
                    view_str = views.view_display_str(view_files)
 
382
                    note("Ignoring files outside view. View is %s" % view_str)
 
383
            return tree, file_list
 
384
        if default_directory == u'.':
 
385
            seed = file_list[0]
 
386
        else:
 
387
            seed = default_directory
 
388
            file_list = [osutils.pathjoin(default_directory, f)
 
389
                         for f in file_list]
 
390
        tree = WorkingTree.open_containing(seed)[0]
 
391
        return tree, tree.safe_relpath_files(file_list, canonicalize,
 
392
                                             apply_view=apply_view)
 
393
 
 
394
    def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
 
395
        """Convert file_list into a list of relpaths in tree.
 
396
 
 
397
        :param self: A tree to operate on.
 
398
        :param file_list: A list of user provided paths or None.
 
399
        :param apply_view: if True and a view is set, apply it or check that
 
400
            specified files are within it
 
401
        :return: A list of relative paths.
 
402
        :raises errors.PathNotChild: When a provided path is in a different self
 
403
            than self.
 
404
        """
 
405
        if file_list is None:
 
406
            return None
 
407
        if self.supports_views() and apply_view:
 
408
            view_files = self.views.lookup_view()
 
409
        else:
 
410
            view_files = []
 
411
        new_list = []
 
412
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
 
413
        # doesn't - fix that up here before we enter the loop.
 
414
        if canonicalize:
 
415
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
 
416
        else:
 
417
            fixer = self.relpath
 
418
        for filename in file_list:
 
419
            relpath = fixer(osutils.dereference_path(filename))
 
420
            if view_files and not osutils.is_inside_any(view_files, relpath):
 
421
                raise errors.FileOutsideView(filename, view_files)
 
422
            new_list.append(relpath)
 
423
        return new_list
 
424
 
 
425
    @staticmethod
354
426
    def open_downlevel(path=None):
355
427
        """Open an unsupported working tree.
356
428
 
369
441
                return True, None
370
442
            else:
371
443
                return True, tree
372
 
        transport = get_transport(location)
373
 
        iterator = bzrdir.BzrDir.find_bzrdirs(transport, evaluate=evaluate,
 
444
        t = transport.get_transport(location)
 
445
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
374
446
                                              list_current=list_current)
375
 
        return [t for t in iterator if t is not None]
 
447
        return [tr for tr in iterator if tr is not None]
376
448
 
377
449
    # should be deprecated - this is slow and in any case treating them as a
378
450
    # container is (we now know) bad style -- mbp 20070302
451
523
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
452
524
 
453
525
    def get_file_with_stat(self, file_id, path=None, filtered=True,
454
 
        _fstat=os.fstat):
 
526
                           _fstat=osutils.fstat):
455
527
        """See Tree.get_file_with_stat."""
456
528
        if path is None:
457
529
            path = self.id2path(file_id)
463
535
        return (file_obj, stat_value)
464
536
 
465
537
    def get_file_text(self, file_id, path=None, filtered=True):
466
 
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
538
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
539
        try:
 
540
            return my_file.read()
 
541
        finally:
 
542
            my_file.close()
467
543
 
468
544
    def get_file_byname(self, filename, filtered=True):
469
545
        path = self.abspath(filename)
523
599
 
524
600
        # Now we have the parents of this content
525
601
        annotator = self.branch.repository.texts.get_annotator()
526
 
        text = self.get_file(file_id).read()
 
602
        text = self.get_file_text(file_id)
527
603
        this_key =(file_id, default_revision)
528
604
        annotator.add_special_text(this_key, file_parent_keys, text)
529
605
        annotations = [(key[-1], line)
812
888
            if revision_id in heads and revision_id not in new_revision_ids:
813
889
                new_revision_ids.append(revision_id)
814
890
        if new_revision_ids != revision_ids:
815
 
            trace.mutter('requested to set revision_ids = %s,'
 
891
            mutter('requested to set revision_ids = %s,'
816
892
                         ' but filtered to %s', revision_ids, new_revision_ids)
817
893
        return new_revision_ids
818
894
 
884
960
    def set_merge_modified(self, modified_hashes):
885
961
        def iter_stanzas():
886
962
            for file_id, hash in modified_hashes.iteritems():
887
 
                yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
 
963
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
964
                    hash=hash)
888
965
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
889
966
 
890
967
    def _sha_from_stat(self, path, stat_result):
899
976
 
900
977
    def _put_rio(self, filename, stanzas, header):
901
978
        self._must_be_locked()
902
 
        my_file = rio_file(stanzas, header)
 
979
        my_file = _mod_rio.rio_file(stanzas, header)
903
980
        self._transport.put_file(filename, my_file,
904
981
            mode=self.bzrdir._get_file_mode())
905
982
 
969
1046
                    raise errors.MergeModifiedFormatError()
970
1047
            except StopIteration:
971
1048
                raise errors.MergeModifiedFormatError()
972
 
            for s in RioReader(hashfile):
 
1049
            for s in _mod_rio.RioReader(hashfile):
973
1050
                # RioReader reads in Unicode, so convert file_ids back to utf8
974
1051
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
975
1052
                if file_id not in self.inventory:
1203
1280
                # absolute path
1204
1281
                fap = from_dir_abspath + '/' + f
1205
1282
 
1206
 
                f_ie = inv.get_child(from_dir_id, f)
 
1283
                dir_ie = inv[from_dir_id]
 
1284
                if dir_ie.kind == 'directory':
 
1285
                    f_ie = dir_ie.children.get(f)
 
1286
                else:
 
1287
                    f_ie = None
1207
1288
                if f_ie:
1208
1289
                    c = 'V'
1209
1290
                elif self.is_ignored(fp[1:]):
1210
1291
                    c = 'I'
1211
1292
                else:
1212
 
                    # we may not have found this file, because of a unicode issue
 
1293
                    # we may not have found this file, because of a unicode
 
1294
                    # issue, or because the directory was actually a symlink.
1213
1295
                    f_norm, can_access = osutils.normalized_filename(f)
1214
1296
                    if f == f_norm or not can_access:
1215
1297
                        # No change, so treat this file normally
1258
1340
                stack.pop()
1259
1341
 
1260
1342
    @needs_tree_write_lock
1261
 
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
 
1343
    def move(self, from_paths, to_dir=None, after=False):
1262
1344
        """Rename files.
1263
1345
 
1264
1346
        to_dir must exist in the inventory.
1298
1380
 
1299
1381
        # check for deprecated use of signature
1300
1382
        if to_dir is None:
1301
 
            to_dir = kwargs.get('to_name', None)
1302
 
            if to_dir is None:
1303
 
                raise TypeError('You must supply a target directory')
1304
 
            else:
1305
 
                symbol_versioning.warn('The parameter to_name was deprecated'
1306
 
                                       ' in version 0.13. Use to_dir instead',
1307
 
                                       DeprecationWarning)
1308
 
 
 
1383
            raise TypeError('You must supply a target directory')
1309
1384
        # check destination directory
1310
1385
        if isinstance(from_paths, basestring):
1311
1386
            raise ValueError()
1320
1395
        to_dir_id = inv.path2id(to_dir)
1321
1396
        if to_dir_id is None:
1322
1397
            raise errors.BzrMoveFailedError('',to_dir,
1323
 
                errors.NotVersionedError(path=str(to_dir)))
 
1398
                errors.NotVersionedError(path=to_dir))
1324
1399
 
1325
1400
        to_dir_ie = inv[to_dir_id]
1326
1401
        if to_dir_ie.kind != 'directory':
1333
1408
            from_id = inv.path2id(from_rel)
1334
1409
            if from_id is None:
1335
1410
                raise errors.BzrMoveFailedError(from_rel,to_dir,
1336
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1411
                    errors.NotVersionedError(path=from_rel))
1337
1412
 
1338
1413
            from_entry = inv[from_id]
1339
1414
            from_parent_id = from_entry.parent_id
1381
1456
            # check the inventory for source and destination
1382
1457
            if from_id is None:
1383
1458
                raise errors.BzrMoveFailedError(from_rel,to_rel,
1384
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1459
                    errors.NotVersionedError(path=from_rel))
1385
1460
            if to_id is not None:
1386
1461
                raise errors.BzrMoveFailedError(from_rel,to_rel,
1387
 
                    errors.AlreadyVersionedError(path=str(to_rel)))
 
1462
                    errors.AlreadyVersionedError(path=to_rel))
1388
1463
 
1389
1464
            # try to determine the mode for rename (only change inv or change
1390
1465
            # inv and file system)
1391
1466
            if after:
1392
1467
                if not self.has_filename(to_rel):
1393
1468
                    raise errors.BzrMoveFailedError(from_id,to_rel,
1394
 
                        errors.NoSuchFile(path=str(to_rel),
 
1469
                        errors.NoSuchFile(path=to_rel,
1395
1470
                        extra="New file has not been created yet"))
1396
1471
                only_change_inv = True
1397
1472
            elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1499
1574
            from_id = basis_tree.path2id(from_rel)
1500
1575
            if from_id is None:
1501
1576
                raise errors.BzrRenameFailedError(from_rel,to_rel,
1502
 
                    errors.NotVersionedError(path=str(from_rel)))
 
1577
                    errors.NotVersionedError(path=from_rel))
1503
1578
            # put entry back in the inventory so we can rename it
1504
1579
            from_entry = basis_tree.inventory[from_id].copy()
1505
1580
            inv.add(from_entry)
1523
1598
        # versioned
1524
1599
        if to_dir_id is None:
1525
1600
            raise errors.BzrMoveFailedError(from_rel,to_rel,
1526
 
                errors.NotVersionedError(path=str(to_dir)))
 
1601
                errors.NotVersionedError(path=to_dir))
1527
1602
 
1528
1603
        # all checks done. now we can continue with our actual work
1529
1604
        mutter('rename_one:\n'
1588
1663
            # - RBC 20060907
1589
1664
            self._write_inventory(self._inventory)
1590
1665
 
1591
 
    def _iter_conflicts(self):
1592
 
        conflicted = set()
1593
 
        for info in self.list_files():
1594
 
            path = info[0]
1595
 
            stem = get_conflicted_stem(path)
1596
 
            if stem is None:
1597
 
                continue
1598
 
            if stem not in conflicted:
1599
 
                conflicted.add(stem)
1600
 
                yield stem
1601
 
 
1602
1666
    @needs_write_lock
1603
1667
    def pull(self, source, overwrite=False, stop_revision=None,
1604
 
             change_reporter=None, possible_transports=None, local=False):
 
1668
             change_reporter=None, possible_transports=None, local=False,
 
1669
             show_base=False):
1605
1670
        source.lock_read()
1606
1671
        try:
1607
1672
            old_revision_info = self.branch.last_revision_info()
1621
1686
                                basis_tree,
1622
1687
                                this_tree=self,
1623
1688
                                pb=None,
1624
 
                                change_reporter=change_reporter)
 
1689
                                change_reporter=change_reporter,
 
1690
                                show_base=show_base)
1625
1691
                    basis_root_id = basis_tree.get_root_id()
1626
1692
                    new_root_id = new_basis_tree.get_root_id()
1627
1693
                    if basis_root_id != new_root_id:
1963
2029
 
1964
2030
        inv_delta = []
1965
2031
 
1966
 
        new_files=set()
 
2032
        all_files = set() # specified and nested files 
1967
2033
        unknown_nested_files=set()
1968
2034
        if to_file is None:
1969
2035
            to_file = sys.stdout
1970
2036
 
 
2037
        files_to_backup = []
 
2038
 
1971
2039
        def recurse_directory_to_add_files(directory):
1972
2040
            # Recurse directory and add all files
1973
2041
            # so we can check if they have changed.
1974
2042
            for parent_info, file_infos in self.walkdirs(directory):
1975
2043
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1976
2044
                    # Is it versioned or ignored?
1977
 
                    if self.path2id(relpath) or self.is_ignored(relpath):
 
2045
                    if self.path2id(relpath):
1978
2046
                        # Add nested content for deletion.
1979
 
                        new_files.add(relpath)
 
2047
                        all_files.add(relpath)
1980
2048
                    else:
1981
 
                        # Files which are not versioned and not ignored
 
2049
                        # Files which are not versioned
1982
2050
                        # should be treated as unknown.
1983
 
                        unknown_nested_files.add((relpath, None, kind))
 
2051
                        files_to_backup.append(relpath)
1984
2052
 
1985
2053
        for filename in files:
1986
2054
            # Get file name into canonical form.
1987
2055
            abspath = self.abspath(filename)
1988
2056
            filename = self.relpath(abspath)
1989
2057
            if len(filename) > 0:
1990
 
                new_files.add(filename)
 
2058
                all_files.add(filename)
1991
2059
                recurse_directory_to_add_files(filename)
1992
2060
 
1993
 
        files = list(new_files)
 
2061
        files = list(all_files)
1994
2062
 
1995
2063
        if len(files) == 0:
1996
2064
            return # nothing to do
2000
2068
 
2001
2069
        # Bail out if we are going to delete files we shouldn't
2002
2070
        if not keep_files and not force:
2003
 
            has_changed_files = len(unknown_nested_files) > 0
2004
 
            if not has_changed_files:
2005
 
                for (file_id, path, content_change, versioned, parent_id, name,
2006
 
                     kind, executable) in self.iter_changes(self.basis_tree(),
2007
 
                         include_unchanged=True, require_versioned=False,
2008
 
                         want_unversioned=True, specific_files=files):
2009
 
                    if versioned == (False, False):
2010
 
                        # The record is unknown ...
2011
 
                        if not self.is_ignored(path[1]):
2012
 
                            # ... but not ignored
2013
 
                            has_changed_files = True
2014
 
                            break
2015
 
                    elif (content_change and (kind[1] is not None) and
2016
 
                            osutils.is_inside_any(files, path[1])):
2017
 
                        # Versioned and changed, but not deleted, and still
2018
 
                        # in one of the dirs to be deleted.
2019
 
                        has_changed_files = True
2020
 
                        break
 
2071
            for (file_id, path, content_change, versioned, parent_id, name,
 
2072
                 kind, executable) in self.iter_changes(self.basis_tree(),
 
2073
                     include_unchanged=True, require_versioned=False,
 
2074
                     want_unversioned=True, specific_files=files):
 
2075
                if versioned[0] == False:
 
2076
                    # The record is unknown or newly added
 
2077
                    files_to_backup.append(path[1])
 
2078
                elif (content_change and (kind[1] is not None) and
 
2079
                        osutils.is_inside_any(files, path[1])):
 
2080
                    # Versioned and changed, but not deleted, and still
 
2081
                    # in one of the dirs to be deleted.
 
2082
                    files_to_backup.append(path[1])
2021
2083
 
2022
 
            if has_changed_files:
2023
 
                # Make delta show ALL applicable changes in error message.
2024
 
                tree_delta = self.changes_from(self.basis_tree(),
2025
 
                    require_versioned=False, want_unversioned=True,
2026
 
                    specific_files=files)
2027
 
                for unknown_file in unknown_nested_files:
2028
 
                    if unknown_file not in tree_delta.unversioned:
2029
 
                        tree_delta.unversioned.extend((unknown_file,))
2030
 
                raise errors.BzrRemoveChangedFilesError(tree_delta)
 
2084
        def backup(file_to_backup):
 
2085
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
 
2086
            osutils.rename(abs_path, self.abspath(backup_name))
 
2087
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
 
2088
                                                         backup_name)
2031
2089
 
2032
2090
        # Build inv_delta and delete files where applicable,
2033
2091
        # do this before any modifications to inventory.
2057
2115
                        len(os.listdir(abs_path)) > 0):
2058
2116
                        if force:
2059
2117
                            osutils.rmtree(abs_path)
 
2118
                            message = "deleted %s" % (f,)
2060
2119
                        else:
2061
 
                            message = "%s is not an empty directory "\
2062
 
                                "and won't be deleted." % (f,)
 
2120
                            message = backup(f)
2063
2121
                    else:
2064
 
                        osutils.delete_any(abs_path)
2065
 
                        message = "deleted %s" % (f,)
 
2122
                        if f in files_to_backup:
 
2123
                            message = backup(f)
 
2124
                        else:
 
2125
                            osutils.delete_any(abs_path)
 
2126
                            message = "deleted %s" % (f,)
2066
2127
                elif message is not None:
2067
2128
                    # Only care if we haven't done anything yet.
2068
2129
                    message = "%s does not exist." % (f,)
2205
2266
    _marker = object()
2206
2267
 
2207
2268
    def update(self, change_reporter=None, possible_transports=None,
2208
 
               revision=None, old_tip=_marker):
 
2269
               revision=None, old_tip=_marker, show_base=False):
2209
2270
        """Update a working tree along its branch.
2210
2271
 
2211
2272
        This will update the branch if its bound too, which means we have
2248
2309
            else:
2249
2310
                if old_tip is self._marker:
2250
2311
                    old_tip = None
2251
 
            return self._update_tree(old_tip, change_reporter, revision)
 
2312
            return self._update_tree(old_tip, change_reporter, revision, show_base)
2252
2313
        finally:
2253
2314
            self.unlock()
2254
2315
 
2255
2316
    @needs_tree_write_lock
2256
 
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
 
2317
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
 
2318
                     show_base=False):
2257
2319
        """Update a tree to the master branch.
2258
2320
 
2259
2321
        :param old_tip: if supplied, the previous tip revision the branch,
2286
2348
            other_tree = self.branch.repository.revision_tree(old_tip)
2287
2349
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
2288
2350
                                             base_tree, this_tree=self,
2289
 
                                             change_reporter=change_reporter)
 
2351
                                             change_reporter=change_reporter,
 
2352
                                             show_base=show_base)
2290
2353
            if nb_conflicts:
2291
2354
                self.add_parent_tree((old_tip, other_tree))
2292
 
                trace.note('Rerun update after fixing the conflicts.')
 
2355
                note('Rerun update after fixing the conflicts.')
2293
2356
                return nb_conflicts
2294
2357
 
2295
2358
        if last_rev != _mod_revision.ensure_null(revision):
2316
2379
 
2317
2380
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2318
2381
                                             this_tree=self,
2319
 
                                             change_reporter=change_reporter)
 
2382
                                             change_reporter=change_reporter,
 
2383
                                             show_base=show_base)
2320
2384
            self.set_last_revision(revision)
2321
2385
            # TODO - dedup parents list with things merged by pull ?
2322
2386
            # reuse the tree we've updated to to set the basis:
2363
2427
    def add_conflicts(self, arg):
2364
2428
        raise errors.UnsupportedOperation(self.add_conflicts, self)
2365
2429
 
2366
 
    @needs_read_lock
2367
2430
    def conflicts(self):
2368
 
        conflicts = _mod_conflicts.ConflictList()
2369
 
        for conflicted in self._iter_conflicts():
2370
 
            text = True
2371
 
            try:
2372
 
                if file_kind(self.abspath(conflicted)) != "file":
2373
 
                    text = False
2374
 
            except errors.NoSuchFile:
2375
 
                text = False
2376
 
            if text is True:
2377
 
                for suffix in ('.THIS', '.OTHER'):
2378
 
                    try:
2379
 
                        kind = file_kind(self.abspath(conflicted+suffix))
2380
 
                        if kind != "file":
2381
 
                            text = False
2382
 
                    except errors.NoSuchFile:
2383
 
                        text = False
2384
 
                    if text == False:
2385
 
                        break
2386
 
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
2387
 
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
2388
 
                             path=conflicted,
2389
 
                             file_id=self.path2id(conflicted)))
2390
 
        return conflicts
 
2431
        raise NotImplementedError(self.conflicts)
2391
2432
 
2392
2433
    def walkdirs(self, prefix=""):
2393
2434
        """Walk the directories of this tree.
2611
2652
        """
2612
2653
        return
2613
2654
 
 
2655
    @needs_read_lock
 
2656
    def check_state(self):
 
2657
        """Check that the working state is/isn't valid."""
 
2658
        check_refs = self._get_check_refs()
 
2659
        refs = {}
 
2660
        for ref in check_refs:
 
2661
            kind, value = ref
 
2662
            if kind == 'trees':
 
2663
                refs[ref] = self.branch.repository.revision_tree(value)
 
2664
        self._check(refs)
 
2665
 
 
2666
    @needs_tree_write_lock
 
2667
    def reset_state(self, revision_ids=None):
 
2668
        """Reset the state of the working tree.
 
2669
 
 
2670
        This does a hard-reset to a last-known-good state. This is a way to
 
2671
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
2672
        """
 
2673
        if revision_ids is None:
 
2674
            revision_ids = self.get_parent_ids()
 
2675
        if not revision_ids:
 
2676
            rt = self.branch.repository.revision_tree(
 
2677
                _mod_revision.NULL_REVISION)
 
2678
        else:
 
2679
            rt = self.branch.repository.revision_tree(revision_ids[0])
 
2680
        self._write_inventory(rt.inventory)
 
2681
        self.set_parent_ids(revision_ids)
 
2682
 
2614
2683
    def _get_rules_searcher(self, default_searcher):
2615
2684
        """See Tree._get_rules_searcher."""
2616
2685
        if self._rules_searcher is None:
2624
2693
        return ShelfManager(self, self._transport)
2625
2694
 
2626
2695
 
2627
 
class WorkingTree2(WorkingTree):
2628
 
    """This is the Format 2 working tree.
2629
 
 
2630
 
    This was the first weave based working tree.
2631
 
     - uses os locks for locking.
2632
 
     - uses the branch last-revision.
2633
 
    """
2634
 
 
2635
 
    def __init__(self, *args, **kwargs):
2636
 
        super(WorkingTree2, self).__init__(*args, **kwargs)
2637
 
        # WorkingTree2 has more of a constraint that self._inventory must
2638
 
        # exist. Because this is an older format, we don't mind the overhead
2639
 
        # caused by the extra computation here.
2640
 
 
2641
 
        # Newer WorkingTree's should only have self._inventory set when they
2642
 
        # have a read lock.
2643
 
        if self._inventory is None:
2644
 
            self.read_working_inventory()
2645
 
 
2646
 
    def _get_check_refs(self):
2647
 
        """Return the references needed to perform a check of this tree."""
2648
 
        return [('trees', self.last_revision())]
2649
 
 
2650
 
    def lock_tree_write(self):
2651
 
        """See WorkingTree.lock_tree_write().
2652
 
 
2653
 
        In Format2 WorkingTrees we have a single lock for the branch and tree
2654
 
        so lock_tree_write() degrades to lock_write().
2655
 
 
2656
 
        :return: An object with an unlock method which will release the lock
2657
 
            obtained.
2658
 
        """
2659
 
        self.branch.lock_write()
2660
 
        try:
2661
 
            self._control_files.lock_write()
2662
 
            return self
2663
 
        except:
2664
 
            self.branch.unlock()
2665
 
            raise
2666
 
 
2667
 
    def unlock(self):
2668
 
        # do non-implementation specific cleanup
2669
 
        self._cleanup()
2670
 
 
2671
 
        # we share control files:
2672
 
        if self._control_files._lock_count == 3:
2673
 
            # _inventory_is_modified is always False during a read lock.
2674
 
            if self._inventory_is_modified:
2675
 
                self.flush()
2676
 
            self._write_hashcache_if_dirty()
2677
 
 
2678
 
        # reverse order of locking.
2679
 
        try:
2680
 
            return self._control_files.unlock()
2681
 
        finally:
2682
 
            self.branch.unlock()
2683
 
 
2684
 
 
2685
2696
class WorkingTree3(WorkingTree):
2686
2697
    """This is the Format 3 working tree.
2687
2698
 
2741
2752
                    raise errors.ConflictFormatError()
2742
2753
            except StopIteration:
2743
2754
                raise errors.ConflictFormatError()
2744
 
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2755
            reader = _mod_rio.RioReader(confile)
 
2756
            return _mod_conflicts.ConflictList.from_stanzas(reader)
2745
2757
        finally:
2746
2758
            confile.close()
2747
2759
 
2760
2772
            self.branch.unlock()
2761
2773
 
2762
2774
 
2763
 
def get_conflicted_stem(path):
2764
 
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2765
 
        if path.endswith(suffix):
2766
 
            return path[:-len(suffix)]
2767
 
 
2768
 
 
2769
 
class WorkingTreeFormat(object):
 
2775
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2776
    """Registry for working tree formats."""
 
2777
 
 
2778
    def __init__(self, other_registry=None):
 
2779
        super(WorkingTreeFormatRegistry, self).__init__(other_registry)
 
2780
        self._default_format = None
 
2781
 
 
2782
    def get_default(self):
 
2783
        """Return the current default format."""
 
2784
        return self._default_format
 
2785
 
 
2786
    def set_default(self, format):
 
2787
        self._default_format = format
 
2788
 
 
2789
 
 
2790
format_registry = WorkingTreeFormatRegistry()
 
2791
 
 
2792
 
 
2793
class WorkingTreeFormat(controldir.ControlComponentFormat):
2770
2794
    """An encapsulation of the initialization and open routines for a format.
2771
2795
 
2772
2796
    Formats provide three things:
2784
2808
    object will be created every time regardless.
2785
2809
    """
2786
2810
 
2787
 
    _default_format = None
2788
 
    """The default format used for new trees."""
2789
 
 
2790
 
    _formats = {}
2791
 
    """The known formats."""
2792
 
 
2793
2811
    requires_rich_root = False
2794
2812
 
2795
2813
    upgrade_recommended = False
2796
2814
 
 
2815
    requires_normalized_unicode_filenames = False
 
2816
 
 
2817
    case_sensitive_filename = "FoRMaT"
 
2818
 
 
2819
    missing_parent_conflicts = False
 
2820
    """If this format supports missing parent conflicts."""
 
2821
 
2797
2822
    @classmethod
2798
2823
    def find_format(klass, a_bzrdir):
2799
2824
        """Return the format for the working tree object in a_bzrdir."""
2800
2825
        try:
2801
2826
            transport = a_bzrdir.get_workingtree_transport(None)
2802
2827
            format_string = transport.get_bytes("format")
2803
 
            return klass._formats[format_string]
 
2828
            return format_registry.get(format_string)
2804
2829
        except errors.NoSuchFile:
2805
2830
            raise errors.NoWorkingTree(base=transport.base)
2806
2831
        except KeyError:
2807
2832
            raise errors.UnknownFormatError(format=format_string,
2808
2833
                                            kind="working tree")
2809
2834
 
 
2835
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
2836
                   accelerator_tree=None, hardlink=False):
 
2837
        """Initialize a new working tree in a_bzrdir.
 
2838
 
 
2839
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
2840
        :param revision_id: allows creating a working tree at a different
 
2841
            revision than the branch is at.
 
2842
        :param from_branch: Branch to checkout
 
2843
        :param accelerator_tree: A tree which can be used for retrieving file
 
2844
            contents more quickly than the revision tree, i.e. a workingtree.
 
2845
            The revision tree will be used for cases where accelerator_tree's
 
2846
            content is different.
 
2847
        :param hardlink: If true, hard-link files from accelerator_tree,
 
2848
            where possible.
 
2849
        """
 
2850
        raise NotImplementedError(self.initialize)
 
2851
 
2810
2852
    def __eq__(self, other):
2811
2853
        return self.__class__ is other.__class__
2812
2854
 
2814
2856
        return not (self == other)
2815
2857
 
2816
2858
    @classmethod
 
2859
    @symbol_versioning.deprecated_method(
 
2860
        symbol_versioning.deprecated_in((2, 4, 0)))
2817
2861
    def get_default_format(klass):
2818
2862
        """Return the current default format."""
2819
 
        return klass._default_format
 
2863
        return format_registry.get_default()
2820
2864
 
2821
2865
    def get_format_string(self):
2822
2866
        """Return the ASCII format string that identifies this format."""
2844
2888
        return False
2845
2889
 
2846
2890
    @classmethod
 
2891
    @symbol_versioning.deprecated_method(
 
2892
        symbol_versioning.deprecated_in((2, 4, 0)))
2847
2893
    def register_format(klass, format):
2848
 
        klass._formats[format.get_format_string()] = format
2849
 
 
2850
 
    @classmethod
 
2894
        format_registry.register(format)
 
2895
 
 
2896
    @classmethod
 
2897
    @symbol_versioning.deprecated_method(
 
2898
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2899
    def register_extra_format(klass, format):
 
2900
        format_registry.register_extra(format)
 
2901
 
 
2902
    @classmethod
 
2903
    @symbol_versioning.deprecated_method(
 
2904
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2905
    def unregister_extra_format(klass, format):
 
2906
        format_registry.unregister_extra(format)
 
2907
 
 
2908
    @classmethod
 
2909
    @symbol_versioning.deprecated_method(
 
2910
        symbol_versioning.deprecated_in((2, 4, 0)))
 
2911
    def get_formats(klass):
 
2912
        return format_registry._get_all()
 
2913
 
 
2914
    @classmethod
 
2915
    @symbol_versioning.deprecated_method(
 
2916
        symbol_versioning.deprecated_in((2, 4, 0)))
2851
2917
    def set_default_format(klass, format):
2852
 
        klass._default_format = format
 
2918
        format_registry.set_default(format)
2853
2919
 
2854
2920
    @classmethod
 
2921
    @symbol_versioning.deprecated_method(
 
2922
        symbol_versioning.deprecated_in((2, 4, 0)))
2855
2923
    def unregister_format(klass, format):
2856
 
        del klass._formats[format.get_format_string()]
2857
 
 
2858
 
 
2859
 
class WorkingTreeFormat2(WorkingTreeFormat):
2860
 
    """The second working tree format.
2861
 
 
2862
 
    This format modified the hash cache from the format 1 hash cache.
2863
 
    """
2864
 
 
2865
 
    upgrade_recommended = True
2866
 
 
2867
 
    def get_format_description(self):
2868
 
        """See WorkingTreeFormat.get_format_description()."""
2869
 
        return "Working tree format 2"
2870
 
 
2871
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2872
 
        """Workaround: create control files for a remote working tree.
2873
 
 
2874
 
        This ensures that it can later be updated and dealt with locally,
2875
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2876
 
        no working tree.  (See bug #43064).
2877
 
        """
2878
 
        sio = StringIO()
2879
 
        inv = inventory.Inventory()
2880
 
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2881
 
        sio.seek(0)
2882
 
        transport.put_file('inventory', sio, file_mode)
2883
 
        transport.put_bytes('pending-merges', '', file_mode)
2884
 
 
2885
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2886
 
                   accelerator_tree=None, hardlink=False):
2887
 
        """See WorkingTreeFormat.initialize()."""
2888
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2889
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2890
 
        if from_branch is not None:
2891
 
            branch = from_branch
2892
 
        else:
2893
 
            branch = a_bzrdir.open_branch()
2894
 
        if revision_id is None:
2895
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2896
 
        branch.lock_write()
2897
 
        try:
2898
 
            branch.generate_revision_history(revision_id)
2899
 
        finally:
2900
 
            branch.unlock()
2901
 
        inv = inventory.Inventory()
2902
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2903
 
                         branch,
2904
 
                         inv,
2905
 
                         _internal=True,
2906
 
                         _format=self,
2907
 
                         _bzrdir=a_bzrdir)
2908
 
        basis_tree = branch.repository.revision_tree(revision_id)
2909
 
        if basis_tree.inventory.root is not None:
2910
 
            wt.set_root_id(basis_tree.get_root_id())
2911
 
        # set the parent list and cache the basis tree.
2912
 
        if _mod_revision.is_null(revision_id):
2913
 
            parent_trees = []
2914
 
        else:
2915
 
            parent_trees = [(revision_id, basis_tree)]
2916
 
        wt.set_parent_trees(parent_trees)
2917
 
        transform.build_tree(basis_tree, wt)
2918
 
        return wt
2919
 
 
2920
 
    def __init__(self):
2921
 
        super(WorkingTreeFormat2, self).__init__()
2922
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
2923
 
 
2924
 
    def open(self, a_bzrdir, _found=False):
2925
 
        """Return the WorkingTree object for a_bzrdir
2926
 
 
2927
 
        _found is a private parameter, do not use it. It is used to indicate
2928
 
               if format probing has already been done.
2929
 
        """
2930
 
        if not _found:
2931
 
            # we are being called directly and must probe.
2932
 
            raise NotImplementedError
2933
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
2934
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2935
 
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2936
 
                           _internal=True,
2937
 
                           _format=self,
2938
 
                           _bzrdir=a_bzrdir)
2939
 
        return wt
 
2924
        format_registry.remove(format)
 
2925
 
2940
2926
 
2941
2927
class WorkingTreeFormat3(WorkingTreeFormat):
2942
2928
    """The second working tree format updated to record a format marker.
2952
2938
 
2953
2939
    upgrade_recommended = True
2954
2940
 
 
2941
    missing_parent_conflicts = True
 
2942
 
2955
2943
    def get_format_string(self):
2956
2944
        """See WorkingTreeFormat.get_format_string()."""
2957
2945
        return "Bazaar-NG Working Tree format 3"
3070
3058
 
3071
3059
 
3072
3060
__default_format = WorkingTreeFormat6()
3073
 
WorkingTreeFormat.register_format(__default_format)
3074
 
WorkingTreeFormat.register_format(WorkingTreeFormat5())
3075
 
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3076
 
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3077
 
WorkingTreeFormat.set_default_format(__default_format)
3078
 
# formats which have no format string are not discoverable
3079
 
# and not independently creatable, so are not registered.
3080
 
_legacy_formats = [WorkingTreeFormat2(),
3081
 
                   ]
 
3061
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
 
3062
    "bzrlib.workingtree_4", "WorkingTreeFormat4")
 
3063
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
 
3064
    "bzrlib.workingtree_4", "WorkingTreeFormat5")
 
3065
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
 
3066
    "bzrlib.workingtree_4", "WorkingTreeFormat6")
 
3067
format_registry.register(WorkingTreeFormat3())
 
3068
format_registry.set_default(__default_format)