~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

merge 2.0 branch rev 4647

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
48
48
import itertools
49
49
import operator
50
50
import stat
51
 
from time import time
52
 
import warnings
53
51
import re
54
52
 
55
53
import bzrlib
57
55
    branch,
58
56
    bzrdir,
59
57
    conflicts as _mod_conflicts,
60
 
    dirstate,
61
58
    errors,
62
59
    generate_ids,
63
60
    globbing,
 
61
    graph as _mod_graph,
64
62
    hashcache,
65
63
    ignores,
 
64
    inventory,
66
65
    merge,
67
66
    revision as _mod_revision,
68
67
    revisiontree,
69
 
    repository,
70
68
    textui,
71
69
    trace,
72
70
    transform,
73
71
    ui,
74
 
    urlutils,
75
72
    views,
76
73
    xml5,
77
 
    xml6,
78
74
    xml7,
79
75
    )
80
76
import bzrlib.branch
81
77
from bzrlib.transport import get_transport
82
 
import bzrlib.ui
83
78
from bzrlib.workingtree_4 import (
84
79
    WorkingTreeFormat4,
85
80
    WorkingTreeFormat5,
89
84
 
90
85
from bzrlib import symbol_versioning
91
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
92
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
93
87
from bzrlib.lockable_files import LockableFiles
94
88
from bzrlib.lockdir import LockDir
95
89
import bzrlib.mutabletree
96
90
from bzrlib.mutabletree import needs_tree_write_lock
97
91
from bzrlib import osutils
98
92
from bzrlib.osutils import (
99
 
    compact_date,
100
93
    file_kind,
101
94
    isdir,
102
95
    normpath,
103
96
    pathjoin,
104
 
    rand_chars,
105
97
    realpath,
106
98
    safe_unicode,
107
99
    splitpath,
111
103
from bzrlib.trace import mutter, note
112
104
from bzrlib.transport.local import LocalTransport
113
105
from bzrlib.progress import DummyProgress, ProgressPhase
114
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
 
106
from bzrlib.revision import CURRENT_REVISION
115
107
from bzrlib.rio import RioReader, rio_file, Stanza
116
 
from bzrlib.symbol_versioning import (deprecated_passed,
117
 
        deprecated_method,
118
 
        deprecated_function,
119
 
        DEPRECATED_PARAMETER,
120
 
        )
 
108
from bzrlib.symbol_versioning import (
 
109
    deprecated_passed,
 
110
    DEPRECATED_PARAMETER,
 
111
    )
121
112
 
122
113
 
123
114
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
290
281
        self._control_files.break_lock()
291
282
        self.branch.break_lock()
292
283
 
 
284
    def _get_check_refs(self):
 
285
        """Return the references needed to perform a check of this tree.
 
286
        
 
287
        The default implementation returns no refs, and is only suitable for
 
288
        trees that have no local caching and can commit on ghosts at any time.
 
289
 
 
290
        :seealso: bzrlib.check for details about check_refs.
 
291
        """
 
292
        return []
 
293
 
293
294
    def requires_rich_root(self):
294
295
        return self._format.requires_rich_root
295
296
 
487
488
        incorrectly attributed to CURRENT_REVISION (but after committing, the
488
489
        attribution will be correct).
489
490
        """
490
 
        basis = self.basis_tree()
491
 
        basis.lock_read()
492
 
        try:
493
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
494
 
                require_versioned=True).next()
495
 
            changed_content, kind = changes[2], changes[6]
496
 
            if not changed_content:
497
 
                return basis.annotate_iter(file_id)
498
 
            if kind[1] is None:
499
 
                return None
500
 
            import annotate
501
 
            if kind[0] != 'file':
502
 
                old_lines = []
503
 
            else:
504
 
                old_lines = list(basis.annotate_iter(file_id))
505
 
            old = [old_lines]
506
 
            for tree in self.branch.repository.revision_trees(
507
 
                self.get_parent_ids()[1:]):
508
 
                if file_id not in tree:
509
 
                    continue
510
 
                old.append(list(tree.annotate_iter(file_id)))
511
 
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
512
 
                                       default_revision)
513
 
        finally:
514
 
            basis.unlock()
 
491
        maybe_file_parent_keys = []
 
492
        for parent_id in self.get_parent_ids():
 
493
            try:
 
494
                parent_tree = self.revision_tree(parent_id)
 
495
            except errors.NoSuchRevisionInTree:
 
496
                parent_tree = self.branch.repository.revision_tree(parent_id)
 
497
            parent_tree.lock_read()
 
498
            try:
 
499
                if file_id not in parent_tree:
 
500
                    continue
 
501
                ie = parent_tree.inventory[file_id]
 
502
                if ie.kind != 'file':
 
503
                    # Note: this is slightly unnecessary, because symlinks and
 
504
                    # directories have a "text" which is the empty text, and we
 
505
                    # know that won't mess up annotations. But it seems cleaner
 
506
                    continue
 
507
                parent_text_key = (file_id, ie.revision)
 
508
                if parent_text_key not in maybe_file_parent_keys:
 
509
                    maybe_file_parent_keys.append(parent_text_key)
 
510
            finally:
 
511
                parent_tree.unlock()
 
512
        graph = _mod_graph.Graph(self.branch.repository.texts)
 
513
        heads = graph.heads(maybe_file_parent_keys)
 
514
        file_parent_keys = []
 
515
        for key in maybe_file_parent_keys:
 
516
            if key in heads:
 
517
                file_parent_keys.append(key)
 
518
 
 
519
        # Now we have the parents of this content
 
520
        annotator = self.branch.repository.texts.get_annotator()
 
521
        text = self.get_file(file_id).read()
 
522
        this_key =(file_id, default_revision)
 
523
        annotator.add_special_text(this_key, file_parent_keys, text)
 
524
        annotations = [(key[-1], line)
 
525
                       for key, line in annotator.annotate_flat(this_key)]
 
526
        return annotations
515
527
 
516
528
    def _get_ancestors(self, default_revision):
517
529
        ancestors = set([default_revision])
601
613
 
602
614
    def get_file_size(self, file_id):
603
615
        """See Tree.get_file_size"""
 
616
        # XXX: this returns the on-disk size; it should probably return the
 
617
        # canonical size
604
618
        try:
605
619
            return os.path.getsize(self.id2abspath(file_id))
606
620
        except OSError, e:
737
751
            raise
738
752
        kind = _mapper(stat_result.st_mode)
739
753
        if kind == 'file':
740
 
            size = stat_result.st_size
741
 
            # try for a stat cache lookup
742
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
743
 
            return (kind, size, executable, self._sha_from_stat(
744
 
                path, stat_result))
 
754
            return self._file_content_summary(path, stat_result)
745
755
        elif kind == 'directory':
746
756
            # perhaps it looks like a plain directory, but it's really a
747
757
            # reference.
754
764
        else:
755
765
            return (kind, None, None, None)
756
766
 
 
767
    def _file_content_summary(self, path, stat_result):
 
768
        size = stat_result.st_size
 
769
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
770
        # try for a stat cache lookup
 
771
        return ('file', size, executable, self._sha_from_stat(
 
772
            path, stat_result))
 
773
 
757
774
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
758
775
        """Common ghost checking functionality from set_parent_*.
759
776
 
889
906
            branch.last_revision().
890
907
        """
891
908
        from bzrlib.merge import Merger, Merge3Merger
892
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
909
        pb = ui.ui_factory.nested_progress_bar()
893
910
        try:
894
911
            merger = Merger(self.branch, this_tree=self, pb=pb)
895
912
            merger.pp = ProgressPhase("Merge phase", 5, pb)
1081
1098
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1082
1099
        else:
1083
1100
            tree_bzrdir = branch_bzrdir
1084
 
        wt = tree_bzrdir.create_workingtree(NULL_REVISION)
 
1101
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1085
1102
        wt.set_parent_ids(self.get_parent_ids())
1086
1103
        my_inv = self.inventory
1087
 
        child_inv = Inventory(root_id=None)
 
1104
        child_inv = inventory.Inventory(root_id=None)
1088
1105
        new_root = my_inv[file_id]
1089
1106
        my_inv.remove_recursive_id(file_id)
1090
1107
        new_root.parent_id = None
1115
1132
    def _kind(self, relpath):
1116
1133
        return osutils.file_kind(self.abspath(relpath))
1117
1134
 
1118
 
    def list_files(self, include_root=False):
1119
 
        """Recursively list all files as (path, class, kind, id, entry).
 
1135
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1136
        """List all files as (path, class, kind, id, entry).
1120
1137
 
1121
1138
        Lists, but does not descend into unversioned directories.
1122
 
 
1123
1139
        This does not include files that have been deleted in this
1124
 
        tree.
 
1140
        tree. Skips the control directory.
1125
1141
 
1126
 
        Skips the control directory.
 
1142
        :param include_root: if True, do not return an entry for the root
 
1143
        :param from_dir: start from this directory or None for the root
 
1144
        :param recursive: whether to recurse into subdirectories or not
1127
1145
        """
1128
1146
        # list_files is an iterator, so @needs_read_lock doesn't work properly
1129
1147
        # with it. So callers should be careful to always read_lock the tree.
1131
1149
            raise errors.ObjectNotLocked(self)
1132
1150
 
1133
1151
        inv = self.inventory
1134
 
        if include_root is True:
 
1152
        if from_dir is None and include_root is True:
1135
1153
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1136
1154
        # Convert these into local objects to save lookup times
1137
1155
        pathjoin = osutils.pathjoin
1144
1162
        fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1145
1163
 
1146
1164
        # directory file_id, relative path, absolute path, reverse sorted children
1147
 
        children = os.listdir(self.basedir)
 
1165
        if from_dir is not None:
 
1166
            from_dir_id = inv.path2id(from_dir)
 
1167
            if from_dir_id is None:
 
1168
                # Directory not versioned
 
1169
                return
 
1170
            from_dir_abspath = pathjoin(self.basedir, from_dir)
 
1171
        else:
 
1172
            from_dir_id = inv.root.file_id
 
1173
            from_dir_abspath = self.basedir
 
1174
        children = os.listdir(from_dir_abspath)
1148
1175
        children.sort()
1149
1176
        # jam 20060527 The kernel sized tree seems equivalent whether we
1150
1177
        # use a deque and popleft to keep them sorted, or if we use a plain
1151
1178
        # list and just reverse() them.
1152
1179
        children = collections.deque(children)
1153
 
        stack = [(inv.root.file_id, u'', self.basedir, children)]
 
1180
        stack = [(from_dir_id, u'', from_dir_abspath, children)]
1154
1181
        while stack:
1155
1182
            from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1156
1183
 
1214
1241
                if fk != 'directory':
1215
1242
                    continue
1216
1243
 
1217
 
                # But do this child first
1218
 
                new_children = os.listdir(fap)
1219
 
                new_children.sort()
1220
 
                new_children = collections.deque(new_children)
1221
 
                stack.append((f_ie.file_id, fp, fap, new_children))
1222
 
                # Break out of inner loop,
1223
 
                # so that we start outer loop with child
1224
 
                break
 
1244
                # But do this child first if recursing down
 
1245
                if recursive:
 
1246
                    new_children = os.listdir(fap)
 
1247
                    new_children.sort()
 
1248
                    new_children = collections.deque(new_children)
 
1249
                    stack.append((f_ie.file_id, fp, fap, new_children))
 
1250
                    # Break out of inner loop,
 
1251
                    # so that we start outer loop with child
 
1252
                    break
1225
1253
            else:
1226
1254
                # if we finished all children, pop it off the stack
1227
1255
                stack.pop()
1405
1433
        inv = self.inventory
1406
1434
        for entry in moved:
1407
1435
            try:
1408
 
                self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
 
1436
                self._move_entry(WorkingTree._RenameEntry(
 
1437
                    entry.to_rel, entry.from_id,
1409
1438
                    entry.to_tail, entry.to_parent_id, entry.from_rel,
1410
1439
                    entry.from_tail, entry.from_parent_id,
1411
1440
                    entry.only_change_inv))
1462
1491
        from_tail = splitpath(from_rel)[-1]
1463
1492
        from_id = inv.path2id(from_rel)
1464
1493
        if from_id is None:
1465
 
            raise errors.BzrRenameFailedError(from_rel,to_rel,
1466
 
                errors.NotVersionedError(path=str(from_rel)))
1467
 
        from_entry = inv[from_id]
 
1494
            # if file is missing in the inventory maybe it's in the basis_tree
 
1495
            basis_tree = self.branch.basis_tree()
 
1496
            from_id = basis_tree.path2id(from_rel)
 
1497
            if from_id is None:
 
1498
                raise errors.BzrRenameFailedError(from_rel,to_rel,
 
1499
                    errors.NotVersionedError(path=str(from_rel)))
 
1500
            # put entry back in the inventory so we can rename it
 
1501
            from_entry = basis_tree.inventory[from_id].copy()
 
1502
            inv.add(from_entry)
 
1503
        else:
 
1504
            from_entry = inv[from_id]
1468
1505
        from_parent_id = from_entry.parent_id
1469
1506
        to_dir, to_tail = os.path.split(to_rel)
1470
1507
        to_dir_id = inv.path2id(to_dir)
1562
1599
    @needs_write_lock
1563
1600
    def pull(self, source, overwrite=False, stop_revision=None,
1564
1601
             change_reporter=None, possible_transports=None, local=False):
1565
 
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1602
        top_pb = ui.ui_factory.nested_progress_bar()
1566
1603
        source.lock_read()
1567
1604
        try:
1568
1605
            pp = ProgressPhase("Pull phase", 2, top_pb)
1576
1613
            if new_revision_info != old_revision_info:
1577
1614
                pp.next_phase()
1578
1615
                repository = self.branch.repository
1579
 
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1616
                pb = ui.ui_factory.nested_progress_bar()
1580
1617
                basis_tree.lock_read()
1581
1618
                try:
1582
1619
                    new_basis_tree = self.branch.basis_tree()
1861
1898
            firstline = xml.split('\n', 1)[0]
1862
1899
            if (not 'revision_id="' in firstline or
1863
1900
                'format="7"' not in firstline):
1864
 
                inv = self.branch.repository.deserialise_inventory(
1865
 
                    new_revision, xml)
 
1901
                inv = self.branch.repository._serializer.read_inventory_from_string(
 
1902
                    xml, new_revision)
1866
1903
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1867
1904
            self._write_basis_inventory(xml)
1868
1905
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
2031
2068
            if filenames is None and len(self.get_parent_ids()) > 1:
2032
2069
                parent_trees = []
2033
2070
                last_revision = self.last_revision()
2034
 
                if last_revision != NULL_REVISION:
 
2071
                if last_revision != _mod_revision.NULL_REVISION:
2035
2072
                    if basis_tree is None:
2036
2073
                        basis_tree = self.basis_tree()
2037
2074
                        basis_tree.lock_read()
2075
2112
    def set_inventory(self, new_inventory_list):
2076
2113
        from bzrlib.inventory import (Inventory,
2077
2114
                                      InventoryDirectory,
2078
 
                                      InventoryEntry,
2079
2115
                                      InventoryFile,
2080
2116
                                      InventoryLink)
2081
2117
        inv = Inventory(self.get_root_id())
2515
2551
        return un_resolved, resolved
2516
2552
 
2517
2553
    @needs_read_lock
2518
 
    def _check(self):
 
2554
    def _check(self, references):
 
2555
        """Check the tree for consistency.
 
2556
 
 
2557
        :param references: A dict with keys matching the items returned by
 
2558
            self._get_check_refs(), and values from looking those keys up in
 
2559
            the repository.
 
2560
        """
2519
2561
        tree_basis = self.basis_tree()
2520
2562
        tree_basis.lock_read()
2521
2563
        try:
2522
 
            repo_basis = self.branch.repository.revision_tree(
2523
 
                self.last_revision())
 
2564
            repo_basis = references[('trees', self.last_revision())]
2524
2565
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2525
2566
                raise errors.BzrCheckError(
2526
2567
                    "Mismatched basis inventory content.")
2572
2613
        if self._inventory is None:
2573
2614
            self.read_working_inventory()
2574
2615
 
 
2616
    def _get_check_refs(self):
 
2617
        """Return the references needed to perform a check of this tree."""
 
2618
        return [('trees', self.last_revision())]
 
2619
 
2575
2620
    def lock_tree_write(self):
2576
2621
        """See WorkingTree.lock_tree_write().
2577
2622
 
2623
2668
 
2624
2669
    def _change_last_revision(self, revision_id):
2625
2670
        """See WorkingTree._change_last_revision."""
2626
 
        if revision_id is None or revision_id == NULL_REVISION:
 
2671
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2627
2672
            try:
2628
2673
                self._transport.delete('last-revision')
2629
2674
            except errors.NoSuchFile:
2634
2679
                mode=self.bzrdir._get_file_mode())
2635
2680
            return True
2636
2681
 
 
2682
    def _get_check_refs(self):
 
2683
        """Return the references needed to perform a check of this tree."""
 
2684
        return [('trees', self.last_revision())]
 
2685
 
2637
2686
    @needs_tree_write_lock
2638
2687
    def set_conflicts(self, conflicts):
2639
2688
        self._put_rio('conflicts', conflicts.to_stanzas(),
2793
2842
        no working tree.  (See bug #43064).
2794
2843
        """
2795
2844
        sio = StringIO()
2796
 
        inv = Inventory()
 
2845
        inv = inventory.Inventory()
2797
2846
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2798
2847
        sio.seek(0)
2799
2848
        transport.put_file('inventory', sio, file_mode)
2815
2864
            branch.generate_revision_history(revision_id)
2816
2865
        finally:
2817
2866
            branch.unlock()
2818
 
        inv = Inventory()
 
2867
        inv = inventory.Inventory()
2819
2868
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2820
2869
                         branch,
2821
2870
                         inv,
2938
2987
            # only set an explicit root id if there is one to set.
2939
2988
            if basis_tree.inventory.root is not None:
2940
2989
                wt.set_root_id(basis_tree.get_root_id())
2941
 
            if revision_id == NULL_REVISION:
 
2990
            if revision_id == _mod_revision.NULL_REVISION:
2942
2991
                wt.set_parent_trees([])
2943
2992
            else:
2944
2993
                wt.set_parent_trees([(revision_id, basis_tree)])
2951
3000
        return wt
2952
3001
 
2953
3002
    def _initial_inventory(self):
2954
 
        return Inventory()
 
3003
        return inventory.Inventory()
2955
3004
 
2956
3005
    def __init__(self):
2957
3006
        super(WorkingTreeFormat3, self).__init__()
2986
3035
        return self.get_format_string()
2987
3036
 
2988
3037
 
2989
 
__default_format = WorkingTreeFormat4()
 
3038
__default_format = WorkingTreeFormat6()
2990
3039
WorkingTreeFormat.register_format(__default_format)
2991
 
WorkingTreeFormat.register_format(WorkingTreeFormat6())
2992
3040
WorkingTreeFormat.register_format(WorkingTreeFormat5())
 
3041
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2993
3042
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2994
3043
WorkingTreeFormat.set_default_format(__default_format)
2995
3044
# formats which have no format string are not discoverable