/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/inventory.py

  • Committer: Martin Pool
  • Date: 2009-03-13 07:54:48 UTC
  • mfrom: (4144 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4189.
  • Revision ID: mbp@sourcefrog.net-20090313075448-jlz1t7baz7gzipqn
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
# FIXME: This refactoring of the workingtree code doesn't seem to keep 
 
17
# FIXME: This refactoring of the workingtree code doesn't seem to keep
18
18
# the WorkingTree's copy of the inventory in sync with the branch.  The
19
19
# branch modifies its working inventory when it does a commit to make
20
20
# missing files permanently removed.
69
69
        file_id of the parent directory, or ROOT_ID
70
70
 
71
71
    revision
72
 
        the revision_id in which this variation of this file was 
 
72
        the revision_id in which this variation of this file was
73
73
        introduced.
74
74
 
75
75
    executable
78
78
 
79
79
    text_sha1
80
80
        sha-1 of the text of the file
81
 
        
 
81
 
82
82
    text_size
83
83
        size in bytes of the text of the file
84
 
        
 
84
 
85
85
    (reading a version 4 tree created a text_id field.)
86
86
 
87
87
    >>> i = Inventory()
94
94
    >>> shouldbe = {0: '', 1: 'src', 2: 'src/hello.c'}
95
95
    >>> for ix, j in enumerate(i.iter_entries()):
96
96
    ...   print (j[0] == shouldbe[ix], j[1])
97
 
    ... 
 
97
    ...
98
98
    (True, InventoryDirectory('TREE_ROOT', u'', parent_id=None, revision=None))
99
99
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None))
100
100
    (True, InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None))
112
112
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None)
113
113
    >>> for path, entry in i.iter_entries():
114
114
    ...     print path
115
 
    ... 
 
115
    ...
116
116
    <BLANKLINE>
117
117
    src
118
118
    src/bye.c
125
125
 
126
126
    # Constants returned by describe_change()
127
127
    #
128
 
    # TODO: These should probably move to some kind of FileChangeDescription 
129
 
    # class; that's like what's inside a TreeDelta but we want to be able to 
 
128
    # TODO: These should probably move to some kind of FileChangeDescription
 
129
    # class; that's like what's inside a TreeDelta but we want to be able to
130
130
    # generate them just for one file at a time.
131
131
    RENAMED = 'renamed'
132
132
    MODIFIED_AND_RENAMED = 'modified and renamed'
133
 
    
 
133
 
134
134
    __slots__ = []
135
135
 
136
136
    def detect_changes(self, old_entry):
137
137
        """Return a (text_modified, meta_modified) from this to old_entry.
138
 
        
139
 
        _read_tree_state must have been called on self and old_entry prior to 
 
138
 
 
139
        _read_tree_state must have been called on self and old_entry prior to
140
140
        calling detect_changes.
141
141
        """
142
142
        return False, False
144
144
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
145
145
             output_to, reverse=False):
146
146
        """Perform a diff between two entries of the same kind."""
147
 
    
 
147
 
148
148
    def parent_candidates(self, previous_inventories):
149
149
        """Find possible per-file graph parents.
150
150
 
163
163
                if ie.revision in candidates:
164
164
                    # same revision value in two different inventories:
165
165
                    # correct possible inconsistencies:
166
 
                    #     * there was a bug in revision updates with 'x' bit 
 
166
                    #     * there was a bug in revision updates with 'x' bit
167
167
                    #       support.
168
168
                    try:
169
169
                        if candidates[ie.revision].executable != ie.executable:
199
199
 
200
200
    def __init__(self, file_id, name, parent_id, text_id=None):
201
201
        """Create an InventoryEntry
202
 
        
 
202
 
203
203
        The filename must be a single component, relative to the
204
204
        parent directory; it cannot be a whole path or relative name.
205
205
 
242
242
    @deprecated_method(deprecated_in((1, 6, 0)))
243
243
    def put_on_disk(self, dest, dp, tree):
244
244
        """Create a representation of self on disk in the prefix dest.
245
 
        
 
245
 
246
246
        This is a template method - implement _put_on_disk in subclasses.
247
247
        """
248
248
        fullpath = osutils.pathjoin(dest, dp)
267
267
        This is a template method, override _check for kind specific
268
268
        tests.
269
269
 
270
 
        :param checker: Check object providing context for the checks; 
 
270
        :param checker: Check object providing context for the checks;
271
271
             can be used to find out what parts of the repository have already
272
272
             been checked.
273
273
        :param rev_id: Revision id from which this InventoryEntry was loaded.
283
283
 
284
284
    def _check(self, checker, rev_id, tree):
285
285
        """Check this inventory entry for kind specific errors."""
286
 
        raise BzrCheckError('unknown entry kind %r in revision {%s}' % 
 
286
        raise BzrCheckError('unknown entry kind %r in revision {%s}' %
287
287
                            (self.kind, rev_id))
288
288
 
289
289
    def copy(self):
293
293
    @staticmethod
294
294
    def describe_change(old_entry, new_entry):
295
295
        """Describe the change between old_entry and this.
296
 
        
 
296
 
297
297
        This smells of being an InterInventoryEntry situation, but as its
298
298
        the first one, we're making it a static method for now.
299
299
 
300
 
        An entry with a different parent, or different name is considered 
 
300
        An entry with a different parent, or different name is considered
301
301
        to be renamed. Reparenting is an internal detail.
302
302
        Note that renaming the parent does not trigger a rename for the
303
303
        child entry itself.
384
384
 
385
385
    def _read_tree_state(self, path, work_tree):
386
386
        """Populate fields in the inventory entry from the given tree.
387
 
        
 
387
 
388
388
        Note that this should be modified to be a noop on virtual trees
389
389
        as all entries created there are prepopulated.
390
390
        """
391
 
        # TODO: Rather than running this manually, we should check the 
 
391
        # TODO: Rather than running this manually, we should check the
392
392
        # working sha1 and other expensive properties when they're
393
393
        # first requested, or preload them if they're already known
394
394
        pass            # nothing to do by default
420
420
    def __eq__(self, other):
421
421
        if not isinstance(other, RootEntry):
422
422
            return NotImplemented
423
 
        
 
423
 
424
424
        return (self.file_id == other.file_id) \
425
425
               and (self.children == other.children)
426
426
 
682
682
 
683
683
 
684
684
class TreeReference(InventoryEntry):
685
 
    
 
685
 
686
686
    kind = 'tree-reference'
687
 
    
 
687
 
688
688
    def __init__(self, file_id, name, parent_id, revision=None,
689
689
                 reference_revision=None):
690
690
        InventoryEntry.__init__(self, file_id, name, parent_id)
702
702
            self.file_id, path)
703
703
 
704
704
    def _forget_tree_state(self):
705
 
        self.reference_revision = None 
 
705
        self.reference_revision = None
706
706
 
707
707
    def _unchanged(self, previous_ie):
708
708
        """See InventoryEntry._unchanged."""
736
736
    'hello.c'
737
737
 
738
738
    May be treated as an iterator or set to look up file ids:
739
 
    
 
739
 
740
740
    >>> bool(inv.path2id('hello.c'))
741
741
    True
742
742
    >>> '123-123' in inv
771
771
        self.revision_id = revision_id
772
772
 
773
773
    def __repr__(self):
774
 
        return "<Inventory object at %x, contents=%r>" % (id(self), self._byid)
 
774
        # More than one page of ouput is not useful anymore to debug
 
775
        max_len = 2048
 
776
        closing = '...}'
 
777
        contents = repr(self._byid)
 
778
        if len(contents) > max_len:
 
779
            contents = contents[:(max_len-len(closing))] + closing
 
780
        return "<Inventory object at %x, contents=%r>" % (id(self), contents)
775
781
 
776
782
    def apply_delta(self, delta):
777
783
        """Apply a delta to this inventory.
785
791
 
786
792
            Each change is a tuple, of the form (old_path, new_path, file_id,
787
793
            new_entry).
788
 
            
 
794
 
789
795
            When new_path is None, the change indicates the removal of an entry
790
796
            from the inventory and new_entry will be ignored (using None is
791
797
            appropriate). If new_path is not None, then new_entry must be an
792
798
            InventoryEntry instance, which will be incorporated into the
793
799
            inventory (and replace any existing entry with the same file id).
794
 
            
 
800
 
795
801
            When old_path is None, the change indicates the addition of
796
802
            a new entry to the inventory.
797
 
            
 
803
 
798
804
            When neither new_path nor old_path are None, the change is a
799
805
            modification to an entry, such as a rename, reparent, kind change
800
 
            etc. 
 
806
            etc.
801
807
 
802
808
            The children attribute of new_entry is ignored. This is because
803
809
            this method preserves children automatically across alterations to
806
812
            change regardless. E.g. in the recursive deletion of a directory -
807
813
            the directory's children must be included in the delta, or the
808
814
            final inventory will be invalid.
 
815
 
 
816
            Note that a file_id must only appear once within a given delta.
 
817
            An AssertionError is raised otherwise.
809
818
        """
 
819
        # Check that the delta is legal. It would be nice if this could be
 
820
        # done within the loops below but it's safer to validate the delta
 
821
        # before starting to mutate the inventory.
 
822
        unique_file_ids = set([f for _, _, f, _ in delta])
 
823
        if len(unique_file_ids) != len(delta):
 
824
            raise AssertionError("a file-id appears multiple times in %r"
 
825
                    % (delta,))
 
826
        del unique_file_ids
 
827
 
810
828
        children = {}
811
829
        # Remove all affected items which were in the original inventory,
812
830
        # starting with the longest paths, thus ensuring parents are examined
876
894
            yield '', self.root
877
895
        elif isinstance(from_dir, basestring):
878
896
            from_dir = self._byid[from_dir]
879
 
            
 
897
 
880
898
        # unrolling the recursive called changed the time from
881
899
        # 440ms/663ms (inline/total) to 116ms/116ms
882
900
        children = from_dir.children.items()
938
956
                file_id = list(specific_file_ids)[0]
939
957
                if file_id in self:
940
958
                    yield self.id2path(file_id), self[file_id]
941
 
                return 
 
959
                return
942
960
            from_dir = self.root
943
961
            if (specific_file_ids is None or yield_parents or
944
962
                self.root.file_id in specific_file_ids):
964
982
                add_ancestors(file_id)
965
983
        else:
966
984
            parents = None
967
 
            
 
985
 
968
986
        stack = [(u'', from_dir)]
969
987
        while stack:
970
988
            cur_relpath, cur_dir = stack.pop()
974
992
 
975
993
                child_relpath = cur_relpath + child_name
976
994
 
977
 
                if (specific_file_ids is None or 
 
995
                if (specific_file_ids is None or
978
996
                    child_ie.file_id in specific_file_ids or
979
997
                    (yield_parents and child_ie.file_id in parents)):
980
998
                    yield child_relpath, child_ie
1012
1030
        accum = []
1013
1031
        def descend(parent_ie, parent_path):
1014
1032
            accum.append((parent_path, parent_ie))
1015
 
            
 
1033
 
1016
1034
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
1017
1035
            kids.sort()
1018
1036
 
1021
1039
                descend(child_ie, child_path)
1022
1040
        descend(self.root, u'')
1023
1041
        return accum
1024
 
        
 
1042
 
1025
1043
    def __contains__(self, file_id):
1026
1044
        """True if this entry contains a file with given id.
1027
1045
 
1100
1118
        The immediate parent must already be versioned.
1101
1119
 
1102
1120
        Returns the new entry object."""
1103
 
        
 
1121
 
1104
1122
        parts = osutils.splitpath(relpath)
1105
1123
 
1106
1124
        if len(parts) == 0:
1186
1204
 
1187
1205
    def id2path(self, file_id):
1188
1206
        """Return as a string the path to file_id.
1189
 
        
 
1207
 
1190
1208
        >>> i = Inventory()
1191
1209
        >>> e = i.add(InventoryDirectory('src-id', 'src', ROOT_ID))
1192
1210
        >>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
1195
1213
        """
1196
1214
        # get all names, skipping root
1197
1215
        return '/'.join(reversed(
1198
 
            [parent.name for parent in 
 
1216
            [parent.name for parent in
1199
1217
             self._iter_file_id_parents(file_id)][:-1]))
1200
 
            
 
1218
 
1201
1219
    def path2id(self, name):
1202
1220
        """Walk down through directories to return entry of last component.
1203
1221
 
1314
1332
 
1315
1333
        del old_parent.children[file_ie.name]
1316
1334
        new_parent.children[new_name] = file_ie
1317
 
        
 
1335
 
1318
1336
        file_ie.name = new_name
1319
1337
        file_ie.parent_id = new_parent_id
1320
1338
 
1375
1393
    global _NAME_RE
1376
1394
    if _NAME_RE is None:
1377
1395
        _NAME_RE = re.compile(r'^[^/\\]+$')
1378
 
        
 
1396
 
1379
1397
    return bool(_NAME_RE.match(name))