~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

  • Committer: Vincent Ladeuil
  • Date: 2013-05-25 17:12:43 UTC
  • mto: (6437.77.1 2.5)
  • mto: This revision was merged to the branch mainline in revision 6577.
  • Revision ID: v.ladeuil+lp@free.fr-20130525171243-au0073fnspecl3kg
Empty arguments in EDITOR are now properly preserved

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2012 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
22
22
WorkingTree.open(dir).
23
23
"""
24
24
 
 
25
from __future__ import absolute_import
 
26
 
25
27
from cStringIO import StringIO
26
28
import os
27
29
import sys
31
33
import errno
32
34
import stat
33
35
 
34
 
import bzrlib
35
36
from bzrlib import (
36
37
    bzrdir,
37
38
    cache_utf8,
 
39
    config,
 
40
    conflicts as _mod_conflicts,
38
41
    debug,
39
42
    dirstate,
40
43
    errors,
 
44
    filters as _mod_filters,
41
45
    generate_ids,
42
46
    osutils,
43
47
    revision as _mod_revision,
46
50
    transform,
47
51
    views,
48
52
    )
49
 
import bzrlib.branch
50
 
import bzrlib.ui
51
53
""")
52
54
 
53
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
 
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
56
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
57
from bzrlib.lock import LogicalLockResult
57
 
from bzrlib.mutabletree import needs_tree_write_lock
 
58
from bzrlib.lockable_files import LockableFiles
 
59
from bzrlib.lockdir import LockDir
 
60
from bzrlib.mutabletree import (
 
61
    MutableTree,
 
62
    needs_tree_write_lock,
 
63
    )
58
64
from bzrlib.osutils import (
59
65
    file_kind,
60
66
    isdir,
62
68
    realpath,
63
69
    safe_unicode,
64
70
    )
65
 
from bzrlib.trace import mutter
66
71
from bzrlib.transport.local import LocalTransport
67
 
from bzrlib.tree import InterTree
68
 
from bzrlib.tree import Tree
69
 
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
70
 
 
71
 
 
72
 
class DirStateWorkingTree(WorkingTree3):
 
72
from bzrlib.tree import (
 
73
    InterTree,
 
74
    InventoryTree,
 
75
    )
 
76
from bzrlib.workingtree import (
 
77
    InventoryWorkingTree,
 
78
    WorkingTree,
 
79
    WorkingTreeFormatMetaDir,
 
80
    )
 
81
 
 
82
 
 
83
class DirStateWorkingTree(InventoryWorkingTree):
 
84
 
73
85
    def __init__(self, basedir,
74
86
                 branch,
75
87
                 _control_files=None,
85
97
        self._format = _format
86
98
        self.bzrdir = _bzrdir
87
99
        basedir = safe_unicode(basedir)
88
 
        mutter("opening working tree %r", basedir)
 
100
        trace.mutter("opening working tree %r", basedir)
89
101
        self._branch = branch
90
102
        self.basedir = realpath(basedir)
91
103
        # if branch is at our basedir and is a format 6 or less
125
137
            state.add(f, file_id, kind, None, '')
126
138
        self._make_dirty(reset_inventory=True)
127
139
 
 
140
    def _get_check_refs(self):
 
141
        """Return the references needed to perform a check of this tree."""
 
142
        return [('trees', self.last_revision())]
 
143
 
128
144
    def _make_dirty(self, reset_inventory):
129
145
        """Make the tree state dirty.
130
146
 
182
198
 
183
199
    def _comparison_data(self, entry, path):
184
200
        kind, executable, stat_value = \
185
 
            WorkingTree3._comparison_data(self, entry, path)
 
201
            WorkingTree._comparison_data(self, entry, path)
186
202
        # it looks like a plain directory, but it's really a reference -- see
187
203
        # also kind()
188
204
        if (self._repo_supports_tree_reference and kind == 'directory'
194
210
    def commit(self, message=None, revprops=None, *args, **kwargs):
195
211
        # mark the tree as dirty post commit - commit
196
212
        # can change the current versioned list by doing deletes.
197
 
        result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
 
213
        result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
198
214
        self._make_dirty(reset_inventory=True)
199
215
        return result
200
216
 
219
235
        local_path = self.bzrdir.get_workingtree_transport(None
220
236
            ).local_abspath('dirstate')
221
237
        self._dirstate = dirstate.DirState.on_file(local_path,
222
 
            self._sha1_provider())
 
238
            self._sha1_provider(), self._worth_saving_limit())
223
239
        return self._dirstate
224
240
 
225
241
    def _sha1_provider(self):
234
250
        else:
235
251
            return None
236
252
 
 
253
    def _worth_saving_limit(self):
 
254
        """How many hash changes are ok before we must save the dirstate.
 
255
 
 
256
        :return: an integer. -1 means never save.
 
257
        """
 
258
        # FIXME: We want a WorkingTreeStack here -- vila 20110812
 
259
        conf = config.BranchStack(self.branch)
 
260
        return conf.get('bzr.workingtree.worth_saving_limit')
 
261
 
237
262
    def filter_unversioned_files(self, paths):
238
263
        """Filter out paths that are versioned.
239
264
 
369
394
        state = self.current_dirstate()
370
395
        if stat_value is None:
371
396
            try:
372
 
                stat_value = os.lstat(file_abspath)
 
397
                stat_value = osutils.lstat(file_abspath)
373
398
            except OSError, e:
374
399
                if e.errno == errno.ENOENT:
375
400
                    return None
456
481
            return False # Missing entries are not executable
457
482
        return entry[1][0][3] # Executable?
458
483
 
459
 
    if not osutils.supports_executable():
460
 
        def is_executable(self, file_id, path=None):
461
 
            """Test if a file is executable or not.
 
484
    def is_executable(self, file_id, path=None):
 
485
        """Test if a file is executable or not.
462
486
 
463
 
            Note: The caller is expected to take a read-lock before calling this.
464
 
            """
 
487
        Note: The caller is expected to take a read-lock before calling this.
 
488
        """
 
489
        if not self._supports_executable():
465
490
            entry = self._get_entry(file_id=file_id, path=path)
466
491
            if entry == (None, None):
467
492
                return False
468
493
            return entry[1][0][3]
469
 
 
470
 
        _is_executable_from_path_and_stat = \
471
 
            _is_executable_from_path_and_stat_from_basis
472
 
    else:
473
 
        def is_executable(self, file_id, path=None):
474
 
            """Test if a file is executable or not.
475
 
 
476
 
            Note: The caller is expected to take a read-lock before calling this.
477
 
            """
 
494
        else:
478
495
            self._must_be_locked()
479
496
            if not path:
480
497
                path = self.id2path(file_id)
481
 
            mode = os.lstat(self.abspath(path)).st_mode
 
498
            mode = osutils.lstat(self.abspath(path)).st_mode
482
499
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
483
500
 
484
501
    def all_file_ids(self):
850
867
                rollback_rename()
851
868
                raise
852
869
            result.append((from_rel, to_rel))
853
 
            state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
 
870
            state._mark_modified()
854
871
            self._make_dirty(reset_inventory=False)
855
872
 
856
873
        return result
949
966
                    all_versioned = False
950
967
                    break
951
968
            if not all_versioned:
952
 
                raise errors.PathsNotVersionedError(paths)
 
969
                raise errors.PathsNotVersionedError(
 
970
                    [p.decode('utf-8') for p in paths])
953
971
        # -- remove redundancy in supplied paths to prevent over-scanning --
954
972
        search_paths = osutils.minimum_path_selection(paths)
955
973
        # sketch:
1004
1022
            found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1005
1023
            for dir_name in split_paths:
1006
1024
                if dir_name not in found_dir_names:
1007
 
                    raise errors.PathsNotVersionedError(paths)
 
1025
                    raise errors.PathsNotVersionedError(
 
1026
                        [p.decode('utf-8') for p in paths])
1008
1027
 
1009
1028
        for dir_name_id, trees_info in found.iteritems():
1010
1029
            for index in search_indexes:
1113
1132
                        _mod_revision.NULL_REVISION)))
1114
1133
                ghosts.append(rev_id)
1115
1134
            accepted_revisions.add(rev_id)
1116
 
        dirstate.set_parent_trees(real_trees, ghosts=ghosts)
 
1135
        updated = False
 
1136
        if (len(real_trees) == 1
 
1137
            and not ghosts
 
1138
            and self.branch.repository._format.fast_deltas
 
1139
            and isinstance(real_trees[0][1],
 
1140
                revisiontree.InventoryRevisionTree)
 
1141
            and self.get_parent_ids()):
 
1142
            rev_id, rev_tree = real_trees[0]
 
1143
            basis_id = self.get_parent_ids()[0]
 
1144
            # There are times when basis_tree won't be in
 
1145
            # self.branch.repository, (switch, for example)
 
1146
            try:
 
1147
                basis_tree = self.branch.repository.revision_tree(basis_id)
 
1148
            except errors.NoSuchRevision:
 
1149
                # Fall back to the set_parent_trees(), since we can't use
 
1150
                # _make_delta if we can't get the RevisionTree
 
1151
                pass
 
1152
            else:
 
1153
                delta = rev_tree.inventory._make_delta(basis_tree.inventory)
 
1154
                dirstate.update_basis_by_delta(delta, rev_id)
 
1155
                updated = True
 
1156
        if not updated:
 
1157
            dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1117
1158
        self._make_dirty(reset_inventory=False)
1118
1159
 
1119
1160
    def _set_root_id(self, file_id):
1139
1180
 
1140
1181
    def unlock(self):
1141
1182
        """Unlock in format 4 trees needs to write the entire dirstate."""
1142
 
        # do non-implementation specific cleanup
1143
 
        self._cleanup()
1144
 
 
1145
1183
        if self._control_files._lock_count == 1:
 
1184
            # do non-implementation specific cleanup
 
1185
            self._cleanup()
 
1186
 
1146
1187
            # eventually we should do signature checking during read locks for
1147
1188
            # dirstate updates.
1148
1189
            if self._control_files._lock_mode == 'w':
1254
1295
    def rename_one(self, from_rel, to_rel, after=False):
1255
1296
        """See WorkingTree.rename_one"""
1256
1297
        self.flush()
1257
 
        WorkingTree.rename_one(self, from_rel, to_rel, after)
 
1298
        super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1258
1299
 
1259
1300
    @needs_tree_write_lock
1260
1301
    def apply_inventory_delta(self, changes):
1293
1334
            self._inventory = inv
1294
1335
        self.flush()
1295
1336
 
 
1337
    @needs_tree_write_lock
 
1338
    def reset_state(self, revision_ids=None):
 
1339
        """Reset the state of the working tree.
 
1340
 
 
1341
        This does a hard-reset to a last-known-good state. This is a way to
 
1342
        fix if something got corrupted (like the .bzr/checkout/dirstate file)
 
1343
        """
 
1344
        if revision_ids is None:
 
1345
            revision_ids = self.get_parent_ids()
 
1346
        if not revision_ids:
 
1347
            base_tree = self.branch.repository.revision_tree(
 
1348
                _mod_revision.NULL_REVISION)
 
1349
            trees = []
 
1350
        else:
 
1351
            trees = zip(revision_ids,
 
1352
                        self.branch.repository.revision_trees(revision_ids))
 
1353
            base_tree = trees[0][1]
 
1354
        state = self.current_dirstate()
 
1355
        # We don't support ghosts yet
 
1356
        state.set_state_from_scratch(base_tree.inventory, trees, [])
 
1357
 
1296
1358
 
1297
1359
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1298
1360
 
1303
1365
        """See dirstate.SHA1Provider.sha1()."""
1304
1366
        filters = self.tree._content_filter_stack(
1305
1367
            self.tree.relpath(osutils.safe_unicode(abspath)))
1306
 
        return internal_size_sha_file_byname(abspath, filters)[1]
 
1368
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1307
1369
 
1308
1370
    def stat_and_sha1(self, abspath):
1309
1371
        """See dirstate.SHA1Provider.stat_and_sha1()."""
1313
1375
        try:
1314
1376
            statvalue = os.fstat(file_obj.fileno())
1315
1377
            if filters:
1316
 
                file_obj = filtered_input_file(file_obj, filters)
 
1378
                file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1317
1379
            sha1 = osutils.size_sha_file(file_obj)[1]
1318
1380
        finally:
1319
1381
            file_obj.close()
1349
1411
class WorkingTree4(DirStateWorkingTree):
1350
1412
    """This is the Format 4 working tree.
1351
1413
 
1352
 
    This differs from WorkingTree3 by:
 
1414
    This differs from WorkingTree by:
1353
1415
     - Having a consolidated internal dirstate, stored in a
1354
1416
       randomly-accessible sorted file on disk.
1355
1417
     - Not having a regular inventory attribute.  One can be synthesized
1383
1445
        return views.PathBasedViews(self)
1384
1446
 
1385
1447
 
1386
 
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
 
1448
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
 
1449
 
 
1450
    missing_parent_conflicts = True
 
1451
 
 
1452
    supports_versioned_directories = True
 
1453
 
 
1454
    _lock_class = LockDir
 
1455
    _lock_file_name = 'lock'
 
1456
 
 
1457
    def _open_control_files(self, a_bzrdir):
 
1458
        transport = a_bzrdir.get_workingtree_transport(None)
 
1459
        return LockableFiles(transport, self._lock_file_name,
 
1460
                             self._lock_class)
1387
1461
 
1388
1462
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1389
1463
                   accelerator_tree=None, hardlink=False):
1390
1464
        """See WorkingTreeFormat.initialize().
1391
1465
 
1392
1466
        :param revision_id: allows creating a working tree at a different
1393
 
        revision than the branch is at.
 
1467
            revision than the branch is at.
1394
1468
        :param accelerator_tree: A tree which can be used for retrieving file
1395
1469
            contents more quickly than the revision tree, i.e. a workingtree.
1396
1470
            The revision tree will be used for cases where accelerator_tree's
1407
1481
        control_files = self._open_control_files(a_bzrdir)
1408
1482
        control_files.create_lock()
1409
1483
        control_files.lock_write()
1410
 
        transport.put_bytes('format', self.get_format_string(),
 
1484
        transport.put_bytes('format', self.as_string(),
1411
1485
            mode=a_bzrdir._get_file_mode())
1412
1486
        if from_branch is not None:
1413
1487
            branch = from_branch
1473
1547
                transform.build_tree(basis, wt, accelerator_tree,
1474
1548
                                     hardlink=hardlink,
1475
1549
                                     delta_from_tree=delta_from_tree)
 
1550
                for hook in MutableTree.hooks['post_build_tree']:
 
1551
                    hook(wt)
1476
1552
            finally:
1477
1553
                basis.unlock()
1478
1554
        finally:
1489
1565
        :param wt: the WorkingTree object
1490
1566
        """
1491
1567
 
 
1568
    def open(self, a_bzrdir, _found=False):
 
1569
        """Return the WorkingTree object for a_bzrdir
 
1570
 
 
1571
        _found is a private parameter, do not use it. It is used to indicate
 
1572
               if format probing has already been done.
 
1573
        """
 
1574
        if not _found:
 
1575
            # we are being called directly and must probe.
 
1576
            raise NotImplementedError
 
1577
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
1578
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
1579
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
1580
        return wt
 
1581
 
1492
1582
    def _open(self, a_bzrdir, control_files):
1493
1583
        """Open the tree itself.
1494
1584
 
1519
1609
    This format:
1520
1610
        - exists within a metadir controlling .bzr
1521
1611
        - includes an explicit version marker for the workingtree control
1522
 
          files, separate from the BzrDir format
 
1612
          files, separate from the ControlDir format
1523
1613
        - modifies the hash cache format
1524
1614
        - is new in bzr 0.15
1525
1615
        - uses a LockDir to guard access to it.
1529
1619
 
1530
1620
    _tree_class = WorkingTree4
1531
1621
 
1532
 
    def get_format_string(self):
 
1622
    @classmethod
 
1623
    def get_format_string(cls):
1533
1624
        """See WorkingTreeFormat.get_format_string()."""
1534
1625
        return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1535
1626
 
1546
1637
 
1547
1638
    _tree_class = WorkingTree5
1548
1639
 
1549
 
    def get_format_string(self):
 
1640
    @classmethod
 
1641
    def get_format_string(cls):
1550
1642
        """See WorkingTreeFormat.get_format_string()."""
1551
1643
        return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1552
1644
 
1566
1658
 
1567
1659
    _tree_class = WorkingTree6
1568
1660
 
1569
 
    def get_format_string(self):
 
1661
    @classmethod
 
1662
    def get_format_string(cls):
1570
1663
        """See WorkingTreeFormat.get_format_string()."""
1571
1664
        return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1572
1665
 
1584
1677
    def supports_views(self):
1585
1678
        return True
1586
1679
 
1587
 
 
1588
 
class DirStateRevisionTree(Tree):
 
1680
    def _get_matchingbzrdir(self):
 
1681
        """Overrideable method to get a bzrdir for testing."""
 
1682
        # We use 'development-subtree' instead of '2a', because we have a
 
1683
        # few tests that want to test tree references
 
1684
        return bzrdir.format_registry.make_bzrdir('development-subtree')
 
1685
 
 
1686
 
 
1687
class DirStateRevisionTree(InventoryTree):
1589
1688
    """A revision tree pulling the inventory from a dirstate.
1590
1689
    
1591
1690
    Note that this is one of the historical (ie revision) trees cached in the
1610
1709
    def annotate_iter(self, file_id,
1611
1710
                      default_revision=_mod_revision.CURRENT_REVISION):
1612
1711
        """See Tree.annotate_iter"""
1613
 
        text_key = (file_id, self.inventory[file_id].revision)
 
1712
        text_key = (file_id, self.get_file_revision(file_id))
1614
1713
        annotations = self._repository.texts.annotate(text_key)
1615
1714
        return [(key[-1], line) for (key, line) in annotations]
1616
1715
 
1617
 
    def _get_ancestors(self, default_revision):
1618
 
        return set(self._repository.get_ancestry(self._revision_id,
1619
 
                                                 topo_sorted=False))
1620
1716
    def _comparison_data(self, entry, path):
1621
1717
        """See Tree._comparison_data."""
1622
1718
        if entry is None:
1763
1859
        # Make sure the file exists
1764
1860
        entry = self._get_entry(file_id, path=path)
1765
1861
        if entry == (None, None): # do we raise?
1766
 
            return None
 
1862
            raise errors.NoSuchId(self, file_id)
1767
1863
        parent_index = self._get_parent_index()
1768
1864
        last_changed_revision = entry[1][parent_index][4]
1769
1865
        try:
1780
1876
            return parent_details[1]
1781
1877
        return None
1782
1878
 
 
1879
    @needs_read_lock
 
1880
    def get_file_revision(self, file_id):
 
1881
        return self.inventory[file_id].revision
 
1882
 
1783
1883
    def get_file(self, file_id, path=None):
1784
1884
        return StringIO(self.get_file_text(file_id))
1785
1885
 
1788
1888
        return self.inventory[file_id].text_size
1789
1889
 
1790
1890
    def get_file_text(self, file_id, path=None):
1791
 
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1792
 
        return ''.join(content)
 
1891
        content = None
 
1892
        for _, content_iter in self.iter_files_bytes([(file_id, None)]):
 
1893
            if content is not None:
 
1894
                raise AssertionError('iter_files_bytes returned'
 
1895
                    ' too many entries')
 
1896
            # For each entry returned by iter_files_bytes, we must consume the
 
1897
            # content_iter before we step the files iterator.
 
1898
            content = ''.join(content_iter)
 
1899
        if content is None:
 
1900
            raise AssertionError('iter_files_bytes did not return'
 
1901
                ' the requested data')
 
1902
        return content
1793
1903
 
1794
1904
    def get_reference_revision(self, file_id, path=None):
1795
1905
        return self.inventory[file_id].reference_revision
1808
1918
                                       identifier))
1809
1919
        return self._repository.iter_files_bytes(repo_desired_files)
1810
1920
 
1811
 
    def get_symlink_target(self, file_id):
 
1921
    def get_symlink_target(self, file_id, path=None):
1812
1922
        entry = self._get_entry(file_id=file_id)
1813
1923
        parent_index = self._get_parent_index()
1814
1924
        if entry[1][parent_index][0] != 'l':
1867
1977
    def is_executable(self, file_id, path=None):
1868
1978
        ie = self.inventory[file_id]
1869
1979
        if ie.kind != "file":
1870
 
            return None
 
1980
            return False
1871
1981
        return ie.executable
1872
1982
 
1873
1983
    def is_locked(self):
1986
2096
    def make_source_parent_tree(source, target):
1987
2097
        """Change the source tree into a parent of the target."""
1988
2098
        revid = source.commit('record tree')
1989
 
        target.branch.repository.fetch(source.branch.repository, revid)
 
2099
        target.branch.fetch(source.branch, revid)
1990
2100
        target.set_parent_ids([revid])
1991
2101
        return target.basis_tree(), target
1992
2102
 
2084
2194
                path_entries = state._entries_for_path(path)
2085
2195
                if not path_entries:
2086
2196
                    # this specified path is not present at all: error
2087
 
                    not_versioned.append(path)
 
2197
                    not_versioned.append(path.decode('utf-8'))
2088
2198
                    continue
2089
2199
                found_versioned = False
2090
2200
                # for each id at this path
2098
2208
                if not found_versioned:
2099
2209
                    # none of the indexes was not 'absent' at all ids for this
2100
2210
                    # path.
2101
 
                    not_versioned.append(path)
 
2211
                    not_versioned.append(path.decode('utf-8'))
2102
2212
            if len(not_versioned) > 0:
2103
2213
                raise errors.PathsNotVersionedError(not_versioned)
2104
2214
        # -- remove redundancy in supplied specific_files to prevent over-scanning --
2171
2281
    def update_format(self, tree):
2172
2282
        """Change the format marker."""
2173
2283
        tree._transport.put_bytes('format',
2174
 
            self.target_format.get_format_string(),
 
2284
            self.target_format.as_string(),
2175
2285
            mode=tree.bzrdir._get_file_mode())
2176
2286
 
2177
2287
 
2194
2304
    def update_format(self, tree):
2195
2305
        """Change the format marker."""
2196
2306
        tree._transport.put_bytes('format',
2197
 
            self.target_format.get_format_string(),
 
2307
            self.target_format.as_string(),
2198
2308
            mode=tree.bzrdir._get_file_mode())
2199
2309
 
2200
2310
 
2223
2333
    def update_format(self, tree):
2224
2334
        """Change the format marker."""
2225
2335
        tree._transport.put_bytes('format',
2226
 
            self.target_format.get_format_string(),
 
2336
            self.target_format.as_string(),
2227
2337
            mode=tree.bzrdir._get_file_mode())