~bzr-pqm/bzr/bzr.dev

138 by mbp at sourcefrog
remove parallel tree from inventory;
1
# (C) 2005 Canonical Ltd
1448 by Robert Collins
revert symlinks correctly
2
#
1 by mbp at sourcefrog
import from baz patch-364
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1448 by Robert Collins
revert symlinks correctly
7
#
1 by mbp at sourcefrog
import from baz patch-364
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1448 by Robert Collins
revert symlinks correctly
12
#
1 by mbp at sourcefrog
import from baz patch-364
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
1197 by Martin Pool
doc
18
# TODO: Maybe also keep the full path of the entry, and the children?
19
# But those depend on its position within a particular inventory, and
20
# it would be nice not to need to hold the backpointer here.
21
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
22
# This should really be an id randomly assigned when the tree is
23
# created, but it's not for now.
24
ROOT_ID = "TREE_ROOT"
25
26
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
27
import os.path
28
import re
29
import sys
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
30
import tarfile
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
31
import types
1 by mbp at sourcefrog
import from baz patch-364
32
800 by Martin Pool
Merge John's import-speedup branch:
33
import bzrlib
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
34
from bzrlib.errors import BzrError, BzrCheckError
70 by mbp at sourcefrog
Prepare for smart recursive add.
35
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
36
from bzrlib.osutils import (pumpfile, quotefn, splitpath, joinpath,
37
                            appendpath, sha_strings)
70 by mbp at sourcefrog
Prepare for smart recursive add.
38
from bzrlib.trace import mutter
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
39
from bzrlib.errors import NotVersionedError
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
40
1 by mbp at sourcefrog
import from baz patch-364
41
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
42
class InventoryEntry(object):
1 by mbp at sourcefrog
import from baz patch-364
43
    """Description of a versioned file.
44
45
    An InventoryEntry has the following fields, which are also
46
    present in the XML inventory-entry element:
47
1197 by Martin Pool
doc
48
    file_id
49
50
    name
51
        (within the parent directory)
52
53
    parent_id
54
        file_id of the parent directory, or ROOT_ID
55
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
56
    revision
1398 by Robert Collins
integrate in Gustavos x-bit patch
57
        the revision_id in which this variation of this file was 
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
58
        introduced.
1197 by Martin Pool
doc
59
1398 by Robert Collins
integrate in Gustavos x-bit patch
60
    executable
61
        Indicates that this file should be executable on systems
62
        that support it.
63
1197 by Martin Pool
doc
64
    text_sha1
65
        sha-1 of the text of the file
66
        
67
    text_size
68
        size in bytes of the text of the file
69
        
70
    (reading a version 4 tree created a text_id field.)
1 by mbp at sourcefrog
import from baz patch-364
71
72
    >>> i = Inventory()
73
    >>> i.path2id('')
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
74
    'TREE_ROOT'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
75
    >>> i.add(InventoryDirectory('123', 'src', ROOT_ID))
76
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT')
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
77
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
78
    InventoryFile('2323', 'hello.c', parent_id='123')
1 by mbp at sourcefrog
import from baz patch-364
79
    >>> for j in i.iter_entries():
80
    ...   print j
81
    ... 
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
82
    ('src', InventoryDirectory('123', 'src', parent_id='TREE_ROOT'))
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
83
    ('src/hello.c', InventoryFile('2323', 'hello.c', parent_id='123'))
84
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
1 by mbp at sourcefrog
import from baz patch-364
85
    Traceback (most recent call last):
86
    ...
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
87
    BzrError: inventory already contains entry with id {2323}
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
88
    >>> i.add(InventoryFile('2324', 'bye.c', '123'))
89
    InventoryFile('2324', 'bye.c', parent_id='123')
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
90
    >>> i.add(InventoryDirectory('2325', 'wibble', '123'))
91
    InventoryDirectory('2325', 'wibble', parent_id='123')
1 by mbp at sourcefrog
import from baz patch-364
92
    >>> i.path2id('src/wibble')
93
    '2325'
94
    >>> '2325' in i
95
    True
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
96
    >>> i.add(InventoryFile('2326', 'wibble.c', '2325'))
97
    InventoryFile('2326', 'wibble.c', parent_id='2325')
1 by mbp at sourcefrog
import from baz patch-364
98
    >>> i['2326']
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
99
    InventoryFile('2326', 'wibble.c', parent_id='2325')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
100
    >>> for path, entry in i.iter_entries():
101
    ...     print path.replace('\\\\', '/')     # for win32 os.sep
102
    ...     assert i.path2id(path)
1 by mbp at sourcefrog
import from baz patch-364
103
    ... 
104
    src
105
    src/bye.c
106
    src/hello.c
107
    src/wibble
108
    src/wibble/wibble.c
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
109
    >>> i.id2path('2326').replace('\\\\', '/')
1 by mbp at sourcefrog
import from baz patch-364
110
    'src/wibble/wibble.c'
111
    """
1197 by Martin Pool
doc
112
    
955 by Martin Pool
- use __slots__ on InventoryEntry; rather faster
113
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
1398 by Robert Collins
integrate in Gustavos x-bit patch
114
                 'text_id', 'parent_id', 'children', 'executable', 
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
115
                 'revision']
1185 by Martin Pool
- add xml round-trip test for revisions
116
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
117
    def _add_text_to_weave(self, new_lines, parents, weave_store, transaction):
118
        weave_store.add_text(self.file_id, self.revision, new_lines, parents,
119
                             transaction)
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
120
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
121
    def detect_changes(self, old_entry):
122
        """Return a (text_modified, meta_modified) from this to old_entry.
123
        
124
        _read_tree_state must have been called on self and old_entry prior to 
125
        calling detect_changes.
126
        """
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
127
        return False, False
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
128
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
129
    def diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
130
             output_to, reverse=False):
131
        """Perform a diff from this to to_entry.
132
133
        text_diff will be used for textual difference calculation.
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
134
        This is a template method, override _diff in child classes.
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
135
        """
1448 by Robert Collins
revert symlinks correctly
136
        self._read_tree_state(tree)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
137
        if to_entry:
138
            # cannot diff from one kind to another - you must do a removal
139
            # and an addif they do not match.
140
            assert self.kind == to_entry.kind
1448 by Robert Collins
revert symlinks correctly
141
            to_entry._read_tree_state(to_tree)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
142
        self._diff(text_diff, from_label, tree, to_label, to_entry, to_tree,
143
                   output_to, reverse)
144
145
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
146
             output_to, reverse=False):
147
        """Perform a diff between two entries of the same kind."""
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
148
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
149
    def find_previous_heads(self, previous_inventories, entry_weave):
1409 by Robert Collins
unify previous inventory entry parent logic in preparation for fixing the revision-thrashing bug
150
        """Return the revisions and entries that directly preceed this.
151
152
        Returned as a map from revision to inventory entry.
153
154
        This is a map containing the file revisions in all parents
155
        for which the file exists, and its revision is not a parent of
156
        any other. If the file is new, the set will be empty.
157
        """
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
158
        def get_ancestors(weave, entry):
159
            return set(map(weave.idx_to_name,
160
                           weave.inclusions([weave.lookup(entry.revision)])))
1409 by Robert Collins
unify previous inventory entry parent logic in preparation for fixing the revision-thrashing bug
161
        heads = {}
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
162
        head_ancestors = {}
1409 by Robert Collins
unify previous inventory entry parent logic in preparation for fixing the revision-thrashing bug
163
        for inv in previous_inventories:
164
            if self.file_id in inv:
165
                ie = inv[self.file_id]
166
                assert ie.file_id == self.file_id
167
                if ie.revision in heads:
1185.1.51 by Robert Collins
merge in reweave support
168
                    # fixup logic, there was a bug in revision updates.
169
                    # with x bit support.
170
                    try:
171
                        if heads[ie.revision].executable != ie.executable:
172
                            heads[ie.revision].executable = False
173
                            ie.executable = False
174
                    except AttributeError:
175
                        pass
1409 by Robert Collins
unify previous inventory entry parent logic in preparation for fixing the revision-thrashing bug
176
                    assert heads[ie.revision] == ie
177
                else:
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
178
                    # may want to add it.
179
                    # may already be covered:
180
                    already_present = 0 != len(
181
                        [head for head in heads 
182
                         if ie.revision in head_ancestors[head]])
183
                    if already_present:
184
                        # an ancestor of a known head.
185
                        continue
186
                    # definately a head:
187
                    ancestors = get_ancestors(entry_weave, ie)
188
                    # may knock something else out:
189
                    check_heads = list(heads.keys())
190
                    for head in check_heads:
191
                        if head in ancestors:
192
                            # this head is not really a head
193
                            heads.pop(head)
194
                    head_ancestors[ie.revision] = ancestors
1409 by Robert Collins
unify previous inventory entry parent logic in preparation for fixing the revision-thrashing bug
195
                    heads[ie.revision] = ie
196
        return heads
197
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
198
    def get_tar_item(self, root, dp, now, tree):
1399.1.7 by Robert Collins
implement symlink exporting to tarballs
199
        """Get a tarfile item and a file stream for its content."""
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
200
        item = tarfile.TarInfo(os.path.join(root, dp))
201
        # TODO: would be cool to actually set it to the timestamp of the
202
        # revision it was last changed
203
        item.mtime = now
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
204
        fileobj = self._put_in_tar(item, tree)
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
205
        return item, fileobj
206
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
207
    def has_text(self):
208
        """Return true if the object this entry represents has textual data.
209
210
        Note that textual data includes binary content.
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
211
212
        Also note that all entries get weave files created for them.
213
        This attribute is primarily used when upgrading from old trees that
214
        did not have the weave index for all inventory entries.
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
215
        """
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
216
        return False
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
217
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
218
    def __init__(self, file_id, name, parent_id, text_id=None):
1 by mbp at sourcefrog
import from baz patch-364
219
        """Create an InventoryEntry
220
        
221
        The filename must be a single component, relative to the
222
        parent directory; it cannot be a whole path or relative name.
223
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
224
        >>> e = InventoryFile('123', 'hello.c', ROOT_ID)
1 by mbp at sourcefrog
import from baz patch-364
225
        >>> e.name
226
        'hello.c'
227
        >>> e.file_id
228
        '123'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
229
        >>> e = InventoryFile('123', 'src/hello.c', ROOT_ID)
1 by mbp at sourcefrog
import from baz patch-364
230
        Traceback (most recent call last):
395 by Martin Pool
- fix error raised from invalid InventoryEntry name
231
        BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
1 by mbp at sourcefrog
import from baz patch-364
232
        """
1183 by Martin Pool
- implement version 5 xml storage, and tests
233
        assert isinstance(name, basestring), name
376 by Martin Pool
- fix slow invariant check when reading in InventoryEntry objects
234
        if '/' in name or '\\' in name:
235
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
1 by mbp at sourcefrog
import from baz patch-364
236
        
1398 by Robert Collins
integrate in Gustavos x-bit patch
237
        self.executable = False
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
238
        self.revision = None
955 by Martin Pool
- use __slots__ on InventoryEntry; rather faster
239
        self.text_sha1 = None
240
        self.text_size = None
1 by mbp at sourcefrog
import from baz patch-364
241
        self.file_id = file_id
242
        self.name = name
243
        self.text_id = text_id
244
        self.parent_id = parent_id
1092.2.6 by Robert Collins
symlink support updated to work
245
        self.symlink_target = None
237 by mbp at sourcefrog
- Better assertions in InventoryEntry constructor
246
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
247
    def kind_character(self):
248
        """Return a short kind indicator useful for appending to names."""
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
249
        raise BzrError('unknown kind %r' % self.kind)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
250
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
251
    known_kinds = ('file', 'directory', 'symlink', 'root_directory')
1 by mbp at sourcefrog
import from baz patch-364
252
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
253
    def _put_in_tar(self, item, tree):
254
        """populate item for stashing in a tar, and return the content stream.
255
256
        If no content is available, return None.
257
        """
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
258
        raise BzrError("don't know how to export {%s} of kind %r" %
259
                       (self.file_id, self.kind))
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
260
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
261
    def put_on_disk(self, dest, dp, tree):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
262
        """Create a representation of self on disk in the prefix dest.
263
        
264
        This is a template method - implement _put_on_disk in subclasses.
265
        """
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
266
        fullpath = appendpath(dest, dp)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
267
        self._put_on_disk(fullpath, tree)
268
        mutter("  export {%s} kind %s to %s" % (self.file_id, self.kind, fullpath))
269
270
    def _put_on_disk(self, fullpath, tree):
271
        """Put this entry onto disk at fullpath, from tree tree."""
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
272
        raise BzrError("don't know how to export {%s} of kind %r" % (self.file_id, self.kind))
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
273
156 by mbp at sourcefrog
new "directories" command
274
    def sorted_children(self):
275
        l = self.children.items()
276
        l.sort()
277
        return l
278
1399.1.1 by Robert Collins
move checks for versionability of file kinds into InventoryEntry
279
    @staticmethod
280
    def versionable_kind(kind):
281
        return kind in ('file', 'directory', 'symlink')
282
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
283
    def check(self, checker, rev_id, inv, tree):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
284
        """Check this inventory entry is intact.
285
286
        This is a template method, override _check for kind specific
287
        tests.
288
        """
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
289
        if self.parent_id != None:
290
            if not inv.has_id(self.parent_id):
291
                raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
292
                        % (self.parent_id, rev_id))
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
293
        self._check(checker, rev_id, tree)
294
295
    def _check(self, checker, rev_id, tree):
296
        """Check this inventory entry for kind specific errors."""
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
297
        raise BzrCheckError('unknown entry kind %r in revision {%s}' % 
298
                            (self.kind, rev_id))
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
299
156 by mbp at sourcefrog
new "directories" command
300
1 by mbp at sourcefrog
import from baz patch-364
301
    def copy(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
302
        """Clone this inventory entry."""
303
        raise NotImplementedError
1 by mbp at sourcefrog
import from baz patch-364
304
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
305
    def _get_snapshot_change(self, previous_entries):
306
        if len(previous_entries) > 1:
307
            return 'merged'
308
        elif len(previous_entries) == 0:
309
            return 'added'
310
        else:
311
            return 'modified/renamed/reparented'
1 by mbp at sourcefrog
import from baz patch-364
312
313
    def __repr__(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
314
        return ("%s(%r, %r, parent_id=%r)"
1 by mbp at sourcefrog
import from baz patch-364
315
                % (self.__class__.__name__,
316
                   self.file_id,
317
                   self.name,
318
                   self.parent_id))
319
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
320
    def snapshot(self, revision, path, previous_entries,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
321
                 work_tree, weave_store, transaction):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
322
        """Make a snapshot of this entry which may or may not have changed.
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
323
        
324
        This means that all its fields are populated, that it has its
325
        text stored in the text store or weave.
326
        """
327
        mutter('new parents of %s are %r', path, previous_entries)
1448 by Robert Collins
revert symlinks correctly
328
        self._read_tree_state(work_tree)
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
329
        if len(previous_entries) == 1:
330
            # cannot be unchanged unless there is only one parent file rev.
331
            parent_ie = previous_entries.values()[0]
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
332
            if self._unchanged(parent_ie):
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
333
                mutter("found unchanged entry")
334
                self.revision = parent_ie.revision
335
                return "unchanged"
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
336
        return self.snapshot_revision(revision, previous_entries, 
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
337
                                      work_tree, weave_store, transaction)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
338
339
    def snapshot_revision(self, revision, previous_entries, work_tree,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
340
                          weave_store, transaction):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
341
        """Record this revision unconditionally."""
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
342
        mutter('new revision for {%s}', self.file_id)
343
        self.revision = revision
344
        change = self._get_snapshot_change(previous_entries)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
345
        self._snapshot_text(previous_entries, work_tree, weave_store,
346
                            transaction)
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
347
        return change
348
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
349
    def _snapshot_text(self, file_parents, work_tree, weave_store, transaction): 
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
350
        """Record the 'text' of this entry, whatever form that takes.
351
        
352
        This default implementation simply adds an empty text.
353
        """
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
354
        mutter('storing file {%s} in revision {%s}',
355
               self.file_id, self.revision)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
356
        self._add_text_to_weave([], file_parents, weave_store, transaction)
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
357
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
358
    def __eq__(self, other):
1 by mbp at sourcefrog
import from baz patch-364
359
        if not isinstance(other, InventoryEntry):
360
            return NotImplemented
361
1398 by Robert Collins
integrate in Gustavos x-bit patch
362
        return ((self.file_id == other.file_id)
363
                and (self.name == other.name)
364
                and (other.symlink_target == self.symlink_target)
365
                and (self.text_sha1 == other.text_sha1)
366
                and (self.text_size == other.text_size)
367
                and (self.text_id == other.text_id)
368
                and (self.parent_id == other.parent_id)
369
                and (self.kind == other.kind)
370
                and (self.revision == other.revision)
371
                and (self.executable == other.executable)
372
                )
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
373
374
    def __ne__(self, other):
375
        return not (self == other)
376
377
    def __hash__(self):
378
        raise ValueError('not hashable')
1 by mbp at sourcefrog
import from baz patch-364
379
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
380
    def _unchanged(self, previous_ie):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
381
        """Has this entry changed relative to previous_ie.
382
383
        This method should be overriden in child classes.
384
        """
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
385
        compatible = True
386
        # different inv parent
387
        if previous_ie.parent_id != self.parent_id:
388
            compatible = False
389
        # renamed
390
        elif previous_ie.name != self.name:
391
            compatible = False
392
        return compatible
393
1448 by Robert Collins
revert symlinks correctly
394
    def _read_tree_state(self, work_tree):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
395
        """Populate fields in the inventory entry from the given tree.
396
        
397
        Note that this should be modified to be a noop on virtual trees
398
        as all entries created there are prepopulated.
399
        """
1185.16.5 by Martin Pool
doc
400
        # TODO: Rather than running this manually, we should check the 
401
        # working sha1 and other expensive properties when they're
402
        # first requested, or preload them if they're already known
403
        pass            # nothing to do by default
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
404
1 by mbp at sourcefrog
import from baz patch-364
405
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
406
class RootEntry(InventoryEntry):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
407
408
    def _check(self, checker, rev_id, tree):
409
        """See InventoryEntry._check"""
410
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
411
    def __init__(self, file_id):
412
        self.file_id = file_id
413
        self.children = {}
414
        self.kind = 'root_directory'
415
        self.parent_id = None
156 by mbp at sourcefrog
new "directories" command
416
        self.name = ''
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
417
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
418
    def __eq__(self, other):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
419
        if not isinstance(other, RootEntry):
420
            return NotImplemented
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
421
        
422
        return (self.file_id == other.file_id) \
423
               and (self.children == other.children)
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
424
425
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
426
class InventoryDirectory(InventoryEntry):
427
    """A directory in an inventory."""
428
429
    def _check(self, checker, rev_id, tree):
430
        """See InventoryEntry._check"""
431
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
432
            raise BzrCheckError('directory {%s} has text in revision {%s}'
433
                                % (self.file_id, rev_id))
434
435
    def copy(self):
436
        other = InventoryDirectory(self.file_id, self.name, self.parent_id)
437
        other.revision = self.revision
438
        # note that children are *not* copied; they're pulled across when
439
        # others are added
440
        return other
441
442
    def __init__(self, file_id, name, parent_id):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
443
        super(InventoryDirectory, self).__init__(file_id, name, parent_id)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
444
        self.children = {}
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
445
        self.kind = 'directory'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
446
447
    def kind_character(self):
448
        """See InventoryEntry.kind_character."""
449
        return '/'
450
451
    def _put_in_tar(self, item, tree):
452
        """See InventoryEntry._put_in_tar."""
453
        item.type = tarfile.DIRTYPE
454
        fileobj = None
455
        item.name += '/'
456
        item.size = 0
457
        item.mode = 0755
458
        return fileobj
459
460
    def _put_on_disk(self, fullpath, tree):
461
        """See InventoryEntry._put_on_disk."""
462
        os.mkdir(fullpath)
463
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
464
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
465
class InventoryFile(InventoryEntry):
466
    """A file in an inventory."""
467
468
    def _check(self, checker, rev_id, tree):
469
        """See InventoryEntry._check"""
470
        revision = self.revision
471
        t = (self.file_id, revision)
472
        if t in checker.checked_texts:
473
            prev_sha = checker.checked_texts[t] 
474
            if prev_sha != self.text_sha1:
475
                raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
476
                                    (self.file_id, rev_id))
477
            else:
478
                checker.repeated_text_cnt += 1
479
                return
480
        mutter('check version {%s} of {%s}', rev_id, self.file_id)
481
        file_lines = tree.get_file_lines(self.file_id)
482
        checker.checked_text_cnt += 1 
483
        if self.text_size != sum(map(len, file_lines)):
484
            raise BzrCheckError('text {%s} wrong size' % self.text_id)
485
        if self.text_sha1 != sha_strings(file_lines):
486
            raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
487
        checker.checked_texts[t] = self.text_sha1
488
489
    def copy(self):
490
        other = InventoryFile(self.file_id, self.name, self.parent_id)
491
        other.executable = self.executable
492
        other.text_id = self.text_id
493
        other.text_sha1 = self.text_sha1
494
        other.text_size = self.text_size
495
        other.revision = self.revision
496
        return other
497
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
498
    def detect_changes(self, old_entry):
499
        """See InventoryEntry.detect_changes."""
500
        assert self.text_sha1 != None
501
        assert old_entry.text_sha1 != None
502
        text_modified = (self.text_sha1 != old_entry.text_sha1)
503
        meta_modified = (self.executable != old_entry.executable)
504
        return text_modified, meta_modified
505
506
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
507
             output_to, reverse=False):
508
        """See InventoryEntry._diff."""
509
        from_text = tree.get_file(self.file_id).readlines()
510
        if to_entry:
511
            to_text = to_tree.get_file(to_entry.file_id).readlines()
512
        else:
513
            to_text = []
514
        if not reverse:
515
            text_diff(from_label, from_text,
516
                      to_label, to_text, output_to)
517
        else:
518
            text_diff(to_label, to_text,
519
                      from_label, from_text, output_to)
520
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
521
    def has_text(self):
522
        """See InventoryEntry.has_text."""
523
        return True
524
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
525
    def __init__(self, file_id, name, parent_id):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
526
        super(InventoryFile, self).__init__(file_id, name, parent_id)
527
        self.kind = 'file'
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
528
529
    def kind_character(self):
530
        """See InventoryEntry.kind_character."""
531
        return ''
532
533
    def _put_in_tar(self, item, tree):
534
        """See InventoryEntry._put_in_tar."""
535
        item.type = tarfile.REGTYPE
536
        fileobj = tree.get_file(self.file_id)
537
        item.size = self.text_size
538
        if tree.is_executable(self.file_id):
539
            item.mode = 0755
540
        else:
541
            item.mode = 0644
542
        return fileobj
543
544
    def _put_on_disk(self, fullpath, tree):
545
        """See InventoryEntry._put_on_disk."""
546
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
547
        if tree.is_executable(self.file_id):
548
            os.chmod(fullpath, 0755)
549
1448 by Robert Collins
revert symlinks correctly
550
    def _read_tree_state(self, work_tree):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
551
        """See InventoryEntry._read_tree_state."""
552
        self.text_sha1 = work_tree.get_file_sha1(self.file_id)
553
        self.executable = work_tree.is_executable(self.file_id)
554
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
555
    def _snapshot_text(self, file_parents, work_tree, weave_store, transaction):
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
556
        """See InventoryEntry._snapshot_text."""
557
        mutter('storing file {%s} in revision {%s}',
558
               self.file_id, self.revision)
559
        # special case to avoid diffing on renames or 
560
        # reparenting
561
        if (len(file_parents) == 1
562
            and self.text_sha1 == file_parents.values()[0].text_sha1
563
            and self.text_size == file_parents.values()[0].text_size):
564
            previous_ie = file_parents.values()[0]
565
            weave_store.add_identical_text(
566
                self.file_id, previous_ie.revision, 
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
567
                self.revision, file_parents, transaction)
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
568
        else:
569
            new_lines = work_tree.get_file(self.file_id).readlines()
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
570
            self._add_text_to_weave(new_lines, file_parents, weave_store,
571
                                    transaction)
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
572
            self.text_sha1 = sha_strings(new_lines)
573
            self.text_size = sum(map(len, new_lines))
574
575
576
    def _unchanged(self, previous_ie):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
577
        """See InventoryEntry._unchanged."""
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
578
        compatible = super(InventoryFile, self)._unchanged(previous_ie)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
579
        if self.text_sha1 != previous_ie.text_sha1:
580
            compatible = False
581
        else:
582
            # FIXME: 20050930 probe for the text size when getting sha1
583
            # in _read_tree_state
584
            self.text_size = previous_ie.text_size
1185.1.52 by Robert Collins
fix detection of changes in inventory entries when the x bit is toggled
585
        if self.executable != previous_ie.executable:
586
            compatible = False
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
587
        return compatible
588
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
589
590
class InventoryLink(InventoryEntry):
591
    """A file in an inventory."""
592
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
593
    __slots__ = ['symlink_target']
594
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
595
    def _check(self, checker, rev_id, tree):
596
        """See InventoryEntry._check"""
597
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
598
            raise BzrCheckError('symlink {%s} has text in revision {%s}'
599
                    % (self.file_id, rev_id))
600
        if self.symlink_target == None:
601
            raise BzrCheckError('symlink {%s} has no target in revision {%s}'
602
                    % (self.file_id, rev_id))
603
604
    def copy(self):
605
        other = InventoryLink(self.file_id, self.name, self.parent_id)
606
        other.symlink_target = self.symlink_target
607
        other.revision = self.revision
608
        return other
609
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
610
    def detect_changes(self, old_entry):
611
        """See InventoryEntry.detect_changes."""
612
        # FIXME: which _modified field should we use ? RBC 20051003
613
        text_modified = (self.symlink_target != old_entry.symlink_target)
614
        if text_modified:
615
            mutter("    symlink target changed")
616
        meta_modified = False
617
        return text_modified, meta_modified
618
619
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
620
             output_to, reverse=False):
621
        """See InventoryEntry._diff."""
622
        from_text = self.symlink_target
623
        if to_entry is not None:
624
            to_text = to_entry.symlink_target
625
            if reverse:
626
                temp = from_text
627
                from_text = to_text
628
                to_text = temp
629
            print >>output_to, '=== target changed %r => %r' % (from_text, to_text)
630
        else:
631
            if not reverse:
632
                print >>output_to, '=== target was %r' % self.symlink_target
633
            else:
634
                print >>output_to, '=== target is %r' % self.symlink_target
635
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
636
    def __init__(self, file_id, name, parent_id):
637
        super(InventoryLink, self).__init__(file_id, name, parent_id)
638
        self.kind = 'symlink'
639
640
    def kind_character(self):
641
        """See InventoryEntry.kind_character."""
642
        return ''
643
644
    def _put_in_tar(self, item, tree):
645
        """See InventoryEntry._put_in_tar."""
646
        iterm.type = tarfile.SYMTYPE
647
        fileobj = None
648
        item.size = 0
649
        item.mode = 0755
650
        item.linkname = self.symlink_target
651
        return fileobj
652
653
    def _put_on_disk(self, fullpath, tree):
654
        """See InventoryEntry._put_on_disk."""
655
        try:
656
            os.symlink(self.symlink_target, fullpath)
657
        except OSError,e:
658
            raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
659
1448 by Robert Collins
revert symlinks correctly
660
    def _read_tree_state(self, work_tree):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
661
        """See InventoryEntry._read_tree_state."""
662
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
663
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
664
    def _unchanged(self, previous_ie):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
665
        """See InventoryEntry._unchanged."""
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
666
        compatible = super(InventoryLink, self)._unchanged(previous_ie)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
667
        if self.symlink_target != previous_ie.symlink_target:
668
            compatible = False
669
        return compatible
670
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
671
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
672
class Inventory(object):
1 by mbp at sourcefrog
import from baz patch-364
673
    """Inventory of versioned files in a tree.
674
240 by mbp at sourcefrog
doc
675
    This describes which file_id is present at each point in the tree,
676
    and possibly the SHA-1 or other information about the file.
677
    Entries can be looked up either by path or by file_id.
1 by mbp at sourcefrog
import from baz patch-364
678
679
    The inventory represents a typical unix file tree, with
680
    directories containing files and subdirectories.  We never store
681
    the full path to a file, because renaming a directory implicitly
682
    moves all of its contents.  This class internally maintains a
683
    lookup tree that allows the children under a directory to be
684
    returned quickly.
685
686
    InventoryEntry objects must not be modified after they are
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
687
    inserted, other than through the Inventory API.
1 by mbp at sourcefrog
import from baz patch-364
688
689
    >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
690
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
691
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
692
    >>> inv['123-123'].name
693
    'hello.c'
694
695
    May be treated as an iterator or set to look up file ids:
696
    
697
    >>> bool(inv.path2id('hello.c'))
698
    True
699
    >>> '123-123' in inv
700
    True
701
702
    May also look up by name:
703
704
    >>> [x[0] for x in inv.iter_entries()]
705
    ['hello.c']
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
706
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
707
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
708
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678')
1 by mbp at sourcefrog
import from baz patch-364
709
    """
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
710
    def __init__(self, root_id=ROOT_ID):
1 by mbp at sourcefrog
import from baz patch-364
711
        """Create or read an inventory.
712
713
        If a working directory is specified, the inventory is read
714
        from there.  If the file is specified, read from that. If not,
715
        the inventory is created empty.
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
716
717
        The inventory is created with a default root directory, with
718
        an id of None.
1 by mbp at sourcefrog
import from baz patch-364
719
        """
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
720
        # We are letting Branch.initialize() create a unique inventory
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
721
        # root id. Rather than generating a random one here.
722
        #if root_id is None:
723
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
724
        self.root = RootEntry(root_id)
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
725
        self._byid = {self.root.file_id: self.root}
1 by mbp at sourcefrog
import from baz patch-364
726
727
1189 by Martin Pool
- BROKEN: partial support for commit into weave
728
    def copy(self):
729
        other = Inventory(self.root.file_id)
1206 by Martin Pool
- fix bug in Inventory.copy()
730
        # copy recursively so we know directories will be added before
731
        # their children.  There are more efficient ways than this...
732
        for path, entry in self.iter_entries():
1189 by Martin Pool
- BROKEN: partial support for commit into weave
733
            if entry == self.root:
734
                continue
735
            other.add(entry.copy())
736
        return other
737
738
1 by mbp at sourcefrog
import from baz patch-364
739
    def __iter__(self):
740
        return iter(self._byid)
741
742
743
    def __len__(self):
744
        """Returns number of entries."""
745
        return len(self._byid)
746
747
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
748
    def iter_entries(self, from_dir=None):
1 by mbp at sourcefrog
import from baz patch-364
749
        """Return (path, entry) pairs, in order by name."""
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
750
        if from_dir == None:
751
            assert self.root
752
            from_dir = self.root
753
        elif isinstance(from_dir, basestring):
754
            from_dir = self._byid[from_dir]
755
            
756
        kids = from_dir.children.items()
1 by mbp at sourcefrog
import from baz patch-364
757
        kids.sort()
758
        for name, ie in kids:
759
            yield name, ie
760
            if ie.kind == 'directory':
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
761
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
271 by Martin Pool
- Windows path fixes
762
                    yield os.path.join(name, cn), cie
555 by Martin Pool
- New Inventory.entries() method
763
764
765
    def entries(self):
766
        """Return list of (path, ie) for all entries except the root.
767
768
        This may be faster than iter_entries.
769
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
770
        accum = []
771
        def descend(dir_ie, dir_path):
556 by Martin Pool
- fix up Inventory.entries()
772
            kids = dir_ie.children.items()
555 by Martin Pool
- New Inventory.entries() method
773
            kids.sort()
774
            for name, ie in kids:
775
                child_path = os.path.join(dir_path, name)
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
776
                accum.append((child_path, ie))
555 by Martin Pool
- New Inventory.entries() method
777
                if ie.kind == 'directory':
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
778
                    descend(ie, child_path)
555 by Martin Pool
- New Inventory.entries() method
779
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
780
        descend(self.root, '')
781
        return accum
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
782
783
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
784
    def directories(self):
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
785
        """Return (path, entry) pairs for all directories, including the root.
1 by mbp at sourcefrog
import from baz patch-364
786
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
787
        accum = []
788
        def descend(parent_ie, parent_path):
789
            accum.append((parent_path, parent_ie))
156 by mbp at sourcefrog
new "directories" command
790
            
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
791
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
792
            kids.sort()
156 by mbp at sourcefrog
new "directories" command
793
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
794
            for name, child_ie in kids:
795
                child_path = os.path.join(parent_path, name)
796
                descend(child_ie, child_path)
797
        descend(self.root, '')
798
        return accum
1 by mbp at sourcefrog
import from baz patch-364
799
        
800
801
802
    def __contains__(self, file_id):
803
        """True if this entry contains a file with given id.
804
805
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
806
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
807
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
808
        >>> '123' in inv
809
        True
810
        >>> '456' in inv
811
        False
812
        """
813
        return file_id in self._byid
814
815
816
    def __getitem__(self, file_id):
817
        """Return the entry for given file_id.
818
819
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
820
        >>> inv.add(InventoryFile('123123', 'hello.c', ROOT_ID))
821
        InventoryFile('123123', 'hello.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
822
        >>> inv['123123'].name
823
        'hello.c'
824
        """
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
825
        try:
826
            return self._byid[file_id]
827
        except KeyError:
380 by Martin Pool
- Slight optimization for Inventory.__getitem__
828
            if file_id == None:
829
                raise BzrError("can't look up file_id None")
830
            else:
831
                raise BzrError("file_id {%s} not in inventory" % file_id)
1 by mbp at sourcefrog
import from baz patch-364
832
833
460 by Martin Pool
- new testing command compare-trees
834
    def get_file_kind(self, file_id):
835
        return self._byid[file_id].kind
836
138 by mbp at sourcefrog
remove parallel tree from inventory;
837
    def get_child(self, parent_id, filename):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
838
        return self[parent_id].children.get(filename)
138 by mbp at sourcefrog
remove parallel tree from inventory;
839
840
1 by mbp at sourcefrog
import from baz patch-364
841
    def add(self, entry):
842
        """Add entry to inventory.
843
844
        To add  a file to a branch ready to be committed, use Branch.add,
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
845
        which calls this.
846
847
        Returns the new entry object.
848
        """
139 by mbp at sourcefrog
simplified/faster Inventory.add
849
        if entry.file_id in self._byid:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
850
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1 by mbp at sourcefrog
import from baz patch-364
851
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
852
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
853
            entry.parent_id = self.root.file_id
854
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
855
        try:
856
            parent = self._byid[entry.parent_id]
857
        except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
858
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
139 by mbp at sourcefrog
simplified/faster Inventory.add
859
860
        if parent.children.has_key(entry.name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
861
            raise BzrError("%s is already versioned" %
140 by mbp at sourcefrog
fix error message for repeated add
862
                    appendpath(self.id2path(parent.file_id), entry.name))
1 by mbp at sourcefrog
import from baz patch-364
863
864
        self._byid[entry.file_id] = entry
139 by mbp at sourcefrog
simplified/faster Inventory.add
865
        parent.children[entry.name] = entry
1092.1.30 by Robert Collins
change smart_add reporting of added files to callback with the entry, and change the inventory.add signature to return the added entry
866
        return entry
1 by mbp at sourcefrog
import from baz patch-364
867
868
70 by mbp at sourcefrog
Prepare for smart recursive add.
869
    def add_path(self, relpath, kind, file_id=None):
870
        """Add entry from a path.
871
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
872
        The immediate parent must already be versioned.
873
874
        Returns the new entry object."""
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
875
        from bzrlib.branch import gen_file_id
1015 by Martin Pool
- fix circular imports
876
        
70 by mbp at sourcefrog
Prepare for smart recursive add.
877
        parts = bzrlib.osutils.splitpath(relpath)
878
        if len(parts) == 0:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
879
            raise BzrError("cannot re-add root of inventory")
70 by mbp at sourcefrog
Prepare for smart recursive add.
880
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
881
        if file_id == None:
800 by Martin Pool
Merge John's import-speedup branch:
882
            file_id = gen_file_id(relpath)
70 by mbp at sourcefrog
Prepare for smart recursive add.
883
753 by Martin Pool
- new exception NotVersionedError
884
        parent_path = parts[:-1]
885
        parent_id = self.path2id(parent_path)
886
        if parent_id == None:
887
            raise NotVersionedError(parent_path)
888
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
889
        if kind == 'directory':
890
            ie = InventoryDirectory(file_id, parts[-1], parent_id)
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
891
        elif kind == 'file':
892
            ie = InventoryFile(file_id, parts[-1], parent_id)
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
893
        elif kind == 'symlink':
894
            ie = InventoryLink(file_id, parts[-1], parent_id)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
895
        else:
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
896
            raise BzrError("unknown kind %r" % kind)
70 by mbp at sourcefrog
Prepare for smart recursive add.
897
        return self.add(ie)
898
899
1 by mbp at sourcefrog
import from baz patch-364
900
    def __delitem__(self, file_id):
901
        """Remove entry by id.
902
903
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
904
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
905
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
906
        >>> '123' in inv
907
        True
908
        >>> del inv['123']
909
        >>> '123' in inv
910
        False
911
        """
912
        ie = self[file_id]
913
138 by mbp at sourcefrog
remove parallel tree from inventory;
914
        assert self[ie.parent_id].children[ie.name] == ie
1 by mbp at sourcefrog
import from baz patch-364
915
        
916
        # TODO: Test deleting all children; maybe hoist to a separate
917
        # deltree method?
918
        if ie.kind == 'directory':
138 by mbp at sourcefrog
remove parallel tree from inventory;
919
            for cie in ie.children.values():
1 by mbp at sourcefrog
import from baz patch-364
920
                del self[cie.file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
921
            del ie.children
1 by mbp at sourcefrog
import from baz patch-364
922
923
        del self._byid[file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
924
        del self[ie.parent_id].children[ie.name]
1 by mbp at sourcefrog
import from baz patch-364
925
926
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
927
    def __eq__(self, other):
1 by mbp at sourcefrog
import from baz patch-364
928
        """Compare two sets by comparing their contents.
929
930
        >>> i1 = Inventory()
931
        >>> i2 = Inventory()
932
        >>> i1 == i2
933
        True
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
934
        >>> i1.add(InventoryFile('123', 'foo', ROOT_ID))
935
        InventoryFile('123', 'foo', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
936
        >>> i1 == i2
937
        False
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
938
        >>> i2.add(InventoryFile('123', 'foo', ROOT_ID))
939
        InventoryFile('123', 'foo', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
940
        >>> i1 == i2
941
        True
942
        """
943
        if not isinstance(other, Inventory):
944
            return NotImplemented
945
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
946
        if len(self._byid) != len(other._byid):
543 by Martin Pool
- More cleanups for set type
947
            # shortcut: obviously not the same
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
948
            return False
949
950
        return self._byid == other._byid
951
952
953
    def __ne__(self, other):
1249 by Martin Pool
- improvements to weave commit [broken]
954
        return not self.__eq__(other)
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
955
956
957
    def __hash__(self):
958
        raise ValueError('not hashable')
959
1 by mbp at sourcefrog
import from baz patch-364
960
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
961
    def get_idpath(self, file_id):
962
        """Return a list of file_ids for the path to an entry.
963
964
        The list contains one element for each directory followed by
965
        the id of the file itself.  So the length of the returned list
966
        is equal to the depth of the file in the tree, counting the
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
967
        root directory as depth 1.
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
968
        """
969
        p = []
970
        while file_id != None:
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
971
            try:
972
                ie = self._byid[file_id]
973
            except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
974
                raise BzrError("file_id {%s} not found in inventory" % file_id)
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
975
            p.insert(0, ie.file_id)
976
            file_id = ie.parent_id
977
        return p
978
979
1 by mbp at sourcefrog
import from baz patch-364
980
    def id2path(self, file_id):
1393.1.32 by Martin Pool
- add docstring demonstrating use of Inventory.id2path
981
        """Return as a list the path to file_id.
982
        
983
        >>> i = Inventory()
984
        >>> e = i.add(InventoryDirectory('src-id', 'src', ROOT_ID))
985
        >>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
986
        >>> print i.id2path('foo-id').replace(os.sep, '/')
987
        src/foo.c
988
        """
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
989
        # get all names, skipping root
922 by Martin Pool
- optimization for Inventory.id2path; access byid map directly rather than
990
        p = [self._byid[fid].name for fid in self.get_idpath(file_id)[1:]]
271 by Martin Pool
- Windows path fixes
991
        return os.sep.join(p)
1 by mbp at sourcefrog
import from baz patch-364
992
            
993
994
995
    def path2id(self, name):
996
        """Walk down through directories to return entry of last component.
997
998
        names may be either a list of path components, or a single
999
        string, in which case it is automatically split.
1000
1001
        This returns the entry of the last component in the path,
1002
        which may be either a file or a directory.
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
1003
1004
        Returns None iff the path is not found.
1 by mbp at sourcefrog
import from baz patch-364
1005
        """
70 by mbp at sourcefrog
Prepare for smart recursive add.
1006
        if isinstance(name, types.StringTypes):
1007
            name = splitpath(name)
1 by mbp at sourcefrog
import from baz patch-364
1008
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
1009
        mutter("lookup path %r" % name)
1010
1011
        parent = self.root
70 by mbp at sourcefrog
Prepare for smart recursive add.
1012
        for f in name:
1 by mbp at sourcefrog
import from baz patch-364
1013
            try:
138 by mbp at sourcefrog
remove parallel tree from inventory;
1014
                cie = parent.children[f]
1 by mbp at sourcefrog
import from baz patch-364
1015
                assert cie.name == f
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
1016
                assert cie.parent_id == parent.file_id
138 by mbp at sourcefrog
remove parallel tree from inventory;
1017
                parent = cie
1 by mbp at sourcefrog
import from baz patch-364
1018
            except KeyError:
1019
                # or raise an error?
1020
                return None
1021
138 by mbp at sourcefrog
remove parallel tree from inventory;
1022
        return parent.file_id
1 by mbp at sourcefrog
import from baz patch-364
1023
1024
1025
    def has_filename(self, names):
1026
        return bool(self.path2id(names))
1027
1028
1029
    def has_id(self, file_id):
1030
        return self._byid.has_key(file_id)
1031
1032
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1033
    def rename(self, file_id, new_parent_id, new_name):
1034
        """Move a file within the inventory.
1035
1036
        This can change either the name, or the parent, or both.
1037
1038
        This does not move the working file."""
1039
        if not is_valid_name(new_name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1040
            raise BzrError("not an acceptable filename: %r" % new_name)
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1041
1042
        new_parent = self._byid[new_parent_id]
1043
        if new_name in new_parent.children:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1044
            raise BzrError("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1045
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
1046
        new_parent_idpath = self.get_idpath(new_parent_id)
1047
        if file_id in new_parent_idpath:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1048
            raise BzrError("cannot move directory %r into a subdirectory of itself, %r"
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
1049
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
1050
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1051
        file_ie = self._byid[file_id]
1052
        old_parent = self._byid[file_ie.parent_id]
1053
1054
        # TODO: Don't leave things messed up if this fails
1055
1056
        del old_parent.children[file_ie.name]
1057
        new_parent.children[new_name] = file_ie
1058
        
1059
        file_ie.name = new_name
1060
        file_ie.parent_id = new_parent_id
1061
1062
1063
1064
1077 by Martin Pool
- avoid compiling REs at module load time
1065
_NAME_RE = None
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1066
1067
def is_valid_name(name):
1077 by Martin Pool
- avoid compiling REs at module load time
1068
    global _NAME_RE
1069
    if _NAME_RE == None:
1070
        _NAME_RE = re.compile(r'^[^/\\]+$')
1071
        
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1072
    return bool(_NAME_RE.match(name))