/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
        """
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
400
1 by mbp at sourcefrog
import from baz patch-364
401
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
402
class RootEntry(InventoryEntry):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
403
404
    def _check(self, checker, rev_id, tree):
405
        """See InventoryEntry._check"""
406
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
407
    def __init__(self, file_id):
408
        self.file_id = file_id
409
        self.children = {}
410
        self.kind = 'root_directory'
411
        self.parent_id = None
156 by mbp at sourcefrog
new "directories" command
412
        self.name = ''
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
413
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
414
    def __eq__(self, other):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
415
        if not isinstance(other, RootEntry):
416
            return NotImplemented
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
417
        
418
        return (self.file_id == other.file_id) \
419
               and (self.children == other.children)
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
420
421
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
422
class InventoryDirectory(InventoryEntry):
423
    """A directory in an inventory."""
424
425
    def _check(self, checker, rev_id, tree):
426
        """See InventoryEntry._check"""
427
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
428
            raise BzrCheckError('directory {%s} has text in revision {%s}'
429
                                % (self.file_id, rev_id))
430
431
    def copy(self):
432
        other = InventoryDirectory(self.file_id, self.name, self.parent_id)
433
        other.revision = self.revision
434
        # note that children are *not* copied; they're pulled across when
435
        # others are added
436
        return other
437
438
    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
439
        super(InventoryDirectory, self).__init__(file_id, name, parent_id)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
440
        self.children = {}
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
441
        self.kind = 'directory'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
442
443
    def kind_character(self):
444
        """See InventoryEntry.kind_character."""
445
        return '/'
446
447
    def _put_in_tar(self, item, tree):
448
        """See InventoryEntry._put_in_tar."""
449
        item.type = tarfile.DIRTYPE
450
        fileobj = None
451
        item.name += '/'
452
        item.size = 0
453
        item.mode = 0755
454
        return fileobj
455
456
    def _put_on_disk(self, fullpath, tree):
457
        """See InventoryEntry._put_on_disk."""
458
        os.mkdir(fullpath)
459
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
460
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
461
class InventoryFile(InventoryEntry):
462
    """A file in an inventory."""
463
464
    def _check(self, checker, rev_id, tree):
465
        """See InventoryEntry._check"""
466
        revision = self.revision
467
        t = (self.file_id, revision)
468
        if t in checker.checked_texts:
469
            prev_sha = checker.checked_texts[t] 
470
            if prev_sha != self.text_sha1:
471
                raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
472
                                    (self.file_id, rev_id))
473
            else:
474
                checker.repeated_text_cnt += 1
475
                return
476
        mutter('check version {%s} of {%s}', rev_id, self.file_id)
477
        file_lines = tree.get_file_lines(self.file_id)
478
        checker.checked_text_cnt += 1 
479
        if self.text_size != sum(map(len, file_lines)):
480
            raise BzrCheckError('text {%s} wrong size' % self.text_id)
481
        if self.text_sha1 != sha_strings(file_lines):
482
            raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
483
        checker.checked_texts[t] = self.text_sha1
484
485
    def copy(self):
486
        other = InventoryFile(self.file_id, self.name, self.parent_id)
487
        other.executable = self.executable
488
        other.text_id = self.text_id
489
        other.text_sha1 = self.text_sha1
490
        other.text_size = self.text_size
491
        other.revision = self.revision
492
        return other
493
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
494
    def detect_changes(self, old_entry):
495
        """See InventoryEntry.detect_changes."""
496
        assert self.text_sha1 != None
497
        assert old_entry.text_sha1 != None
498
        text_modified = (self.text_sha1 != old_entry.text_sha1)
499
        meta_modified = (self.executable != old_entry.executable)
500
        return text_modified, meta_modified
501
502
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
503
             output_to, reverse=False):
504
        """See InventoryEntry._diff."""
505
        from_text = tree.get_file(self.file_id).readlines()
506
        if to_entry:
507
            to_text = to_tree.get_file(to_entry.file_id).readlines()
508
        else:
509
            to_text = []
510
        if not reverse:
511
            text_diff(from_label, from_text,
512
                      to_label, to_text, output_to)
513
        else:
514
            text_diff(to_label, to_text,
515
                      from_label, from_text, output_to)
516
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
517
    def has_text(self):
518
        """See InventoryEntry.has_text."""
519
        return True
520
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
521
    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
522
        super(InventoryFile, self).__init__(file_id, name, parent_id)
523
        self.kind = 'file'
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
524
525
    def kind_character(self):
526
        """See InventoryEntry.kind_character."""
527
        return ''
528
529
    def _put_in_tar(self, item, tree):
530
        """See InventoryEntry._put_in_tar."""
531
        item.type = tarfile.REGTYPE
532
        fileobj = tree.get_file(self.file_id)
533
        item.size = self.text_size
534
        if tree.is_executable(self.file_id):
535
            item.mode = 0755
536
        else:
537
            item.mode = 0644
538
        return fileobj
539
540
    def _put_on_disk(self, fullpath, tree):
541
        """See InventoryEntry._put_on_disk."""
542
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
543
        if tree.is_executable(self.file_id):
544
            os.chmod(fullpath, 0755)
545
1448 by Robert Collins
revert symlinks correctly
546
    def _read_tree_state(self, work_tree):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
547
        """See InventoryEntry._read_tree_state."""
548
        self.text_sha1 = work_tree.get_file_sha1(self.file_id)
549
        self.executable = work_tree.is_executable(self.file_id)
550
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
551
    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
552
        """See InventoryEntry._snapshot_text."""
553
        mutter('storing file {%s} in revision {%s}',
554
               self.file_id, self.revision)
555
        # special case to avoid diffing on renames or 
556
        # reparenting
557
        if (len(file_parents) == 1
558
            and self.text_sha1 == file_parents.values()[0].text_sha1
559
            and self.text_size == file_parents.values()[0].text_size):
560
            previous_ie = file_parents.values()[0]
561
            weave_store.add_identical_text(
562
                self.file_id, previous_ie.revision, 
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
563
                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
564
        else:
565
            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
566
            self._add_text_to_weave(new_lines, file_parents, weave_store,
567
                                    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
            self.text_sha1 = sha_strings(new_lines)
569
            self.text_size = sum(map(len, new_lines))
570
571
572
    def _unchanged(self, previous_ie):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
573
        """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
574
        compatible = super(InventoryFile, self)._unchanged(previous_ie)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
575
        if self.text_sha1 != previous_ie.text_sha1:
576
            compatible = False
577
        else:
578
            # FIXME: 20050930 probe for the text size when getting sha1
579
            # in _read_tree_state
580
            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
581
        if self.executable != previous_ie.executable:
582
            compatible = False
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
583
        return compatible
584
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
585
586
class InventoryLink(InventoryEntry):
587
    """A file in an inventory."""
588
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
589
    __slots__ = ['symlink_target']
590
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
591
    def _check(self, checker, rev_id, tree):
592
        """See InventoryEntry._check"""
593
        if self.text_sha1 != None or self.text_size != None or self.text_id != None:
594
            raise BzrCheckError('symlink {%s} has text in revision {%s}'
595
                    % (self.file_id, rev_id))
596
        if self.symlink_target == None:
597
            raise BzrCheckError('symlink {%s} has no target in revision {%s}'
598
                    % (self.file_id, rev_id))
599
600
    def copy(self):
601
        other = InventoryLink(self.file_id, self.name, self.parent_id)
602
        other.symlink_target = self.symlink_target
603
        other.revision = self.revision
604
        return other
605
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
606
    def detect_changes(self, old_entry):
607
        """See InventoryEntry.detect_changes."""
608
        # FIXME: which _modified field should we use ? RBC 20051003
609
        text_modified = (self.symlink_target != old_entry.symlink_target)
610
        if text_modified:
611
            mutter("    symlink target changed")
612
        meta_modified = False
613
        return text_modified, meta_modified
614
615
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
616
             output_to, reverse=False):
617
        """See InventoryEntry._diff."""
618
        from_text = self.symlink_target
619
        if to_entry is not None:
620
            to_text = to_entry.symlink_target
621
            if reverse:
622
                temp = from_text
623
                from_text = to_text
624
                to_text = temp
625
            print >>output_to, '=== target changed %r => %r' % (from_text, to_text)
626
        else:
627
            if not reverse:
628
                print >>output_to, '=== target was %r' % self.symlink_target
629
            else:
630
                print >>output_to, '=== target is %r' % self.symlink_target
631
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
632
    def __init__(self, file_id, name, parent_id):
633
        super(InventoryLink, self).__init__(file_id, name, parent_id)
634
        self.kind = 'symlink'
635
636
    def kind_character(self):
637
        """See InventoryEntry.kind_character."""
638
        return ''
639
640
    def _put_in_tar(self, item, tree):
641
        """See InventoryEntry._put_in_tar."""
642
        iterm.type = tarfile.SYMTYPE
643
        fileobj = None
644
        item.size = 0
645
        item.mode = 0755
646
        item.linkname = self.symlink_target
647
        return fileobj
648
649
    def _put_on_disk(self, fullpath, tree):
650
        """See InventoryEntry._put_on_disk."""
651
        try:
652
            os.symlink(self.symlink_target, fullpath)
653
        except OSError,e:
654
            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
655
1448 by Robert Collins
revert symlinks correctly
656
    def _read_tree_state(self, work_tree):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
657
        """See InventoryEntry._read_tree_state."""
658
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
659
1405 by Robert Collins
remove some of the upgrade code that was duplicated with inventory_entry, and give all inventory entries a weave
660
    def _unchanged(self, previous_ie):
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
661
        """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
662
        compatible = super(InventoryLink, self)._unchanged(previous_ie)
1399.1.11 by Robert Collins
finish off nearly all of the kind separations
663
        if self.symlink_target != previous_ie.symlink_target:
664
            compatible = False
665
        return compatible
666
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
667
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
668
class Inventory(object):
1 by mbp at sourcefrog
import from baz patch-364
669
    """Inventory of versioned files in a tree.
670
240 by mbp at sourcefrog
doc
671
    This describes which file_id is present at each point in the tree,
672
    and possibly the SHA-1 or other information about the file.
673
    Entries can be looked up either by path or by file_id.
1 by mbp at sourcefrog
import from baz patch-364
674
675
    The inventory represents a typical unix file tree, with
676
    directories containing files and subdirectories.  We never store
677
    the full path to a file, because renaming a directory implicitly
678
    moves all of its contents.  This class internally maintains a
679
    lookup tree that allows the children under a directory to be
680
    returned quickly.
681
682
    InventoryEntry objects must not be modified after they are
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
683
    inserted, other than through the Inventory API.
1 by mbp at sourcefrog
import from baz patch-364
684
685
    >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
686
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
687
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
688
    >>> inv['123-123'].name
689
    'hello.c'
690
691
    May be treated as an iterator or set to look up file ids:
692
    
693
    >>> bool(inv.path2id('hello.c'))
694
    True
695
    >>> '123-123' in inv
696
    True
697
698
    May also look up by name:
699
700
    >>> [x[0] for x in inv.iter_entries()]
701
    ['hello.c']
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
702
    >>> 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
703
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
704
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678')
1 by mbp at sourcefrog
import from baz patch-364
705
    """
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
706
    def __init__(self, root_id=ROOT_ID):
1 by mbp at sourcefrog
import from baz patch-364
707
        """Create or read an inventory.
708
709
        If a working directory is specified, the inventory is read
710
        from there.  If the file is specified, read from that. If not,
711
        the inventory is created empty.
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
712
713
        The inventory is created with a default root directory, with
714
        an id of None.
1 by mbp at sourcefrog
import from baz patch-364
715
        """
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
716
        # 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
717
        # root id. Rather than generating a random one here.
718
        #if root_id is None:
719
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
720
        self.root = RootEntry(root_id)
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
721
        self._byid = {self.root.file_id: self.root}
1 by mbp at sourcefrog
import from baz patch-364
722
723
1189 by Martin Pool
- BROKEN: partial support for commit into weave
724
    def copy(self):
725
        other = Inventory(self.root.file_id)
1206 by Martin Pool
- fix bug in Inventory.copy()
726
        # copy recursively so we know directories will be added before
727
        # their children.  There are more efficient ways than this...
728
        for path, entry in self.iter_entries():
1189 by Martin Pool
- BROKEN: partial support for commit into weave
729
            if entry == self.root:
730
                continue
731
            other.add(entry.copy())
732
        return other
733
734
1 by mbp at sourcefrog
import from baz patch-364
735
    def __iter__(self):
736
        return iter(self._byid)
737
738
739
    def __len__(self):
740
        """Returns number of entries."""
741
        return len(self._byid)
742
743
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
744
    def iter_entries(self, from_dir=None):
1 by mbp at sourcefrog
import from baz patch-364
745
        """Return (path, entry) pairs, in order by name."""
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
746
        if from_dir == None:
747
            assert self.root
748
            from_dir = self.root
749
        elif isinstance(from_dir, basestring):
750
            from_dir = self._byid[from_dir]
751
            
752
        kids = from_dir.children.items()
1 by mbp at sourcefrog
import from baz patch-364
753
        kids.sort()
754
        for name, ie in kids:
755
            yield name, ie
756
            if ie.kind == 'directory':
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
757
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
271 by Martin Pool
- Windows path fixes
758
                    yield os.path.join(name, cn), cie
555 by Martin Pool
- New Inventory.entries() method
759
760
761
    def entries(self):
762
        """Return list of (path, ie) for all entries except the root.
763
764
        This may be faster than iter_entries.
765
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
766
        accum = []
767
        def descend(dir_ie, dir_path):
556 by Martin Pool
- fix up Inventory.entries()
768
            kids = dir_ie.children.items()
555 by Martin Pool
- New Inventory.entries() method
769
            kids.sort()
770
            for name, ie in kids:
771
                child_path = os.path.join(dir_path, name)
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
772
                accum.append((child_path, ie))
555 by Martin Pool
- New Inventory.entries() method
773
                if ie.kind == 'directory':
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
774
                    descend(ie, child_path)
555 by Martin Pool
- New Inventory.entries() method
775
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
776
        descend(self.root, '')
777
        return accum
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
778
779
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
780
    def directories(self):
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
781
        """Return (path, entry) pairs for all directories, including the root.
1 by mbp at sourcefrog
import from baz patch-364
782
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
783
        accum = []
784
        def descend(parent_ie, parent_path):
785
            accum.append((parent_path, parent_ie))
156 by mbp at sourcefrog
new "directories" command
786
            
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
787
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
788
            kids.sort()
156 by mbp at sourcefrog
new "directories" command
789
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
790
            for name, child_ie in kids:
791
                child_path = os.path.join(parent_path, name)
792
                descend(child_ie, child_path)
793
        descend(self.root, '')
794
        return accum
1 by mbp at sourcefrog
import from baz patch-364
795
        
796
797
798
    def __contains__(self, file_id):
799
        """True if this entry contains a file with given id.
800
801
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
802
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
803
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
804
        >>> '123' in inv
805
        True
806
        >>> '456' in inv
807
        False
808
        """
809
        return file_id in self._byid
810
811
812
    def __getitem__(self, file_id):
813
        """Return the entry for given file_id.
814
815
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
816
        >>> inv.add(InventoryFile('123123', 'hello.c', ROOT_ID))
817
        InventoryFile('123123', 'hello.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
818
        >>> inv['123123'].name
819
        'hello.c'
820
        """
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
821
        try:
822
            return self._byid[file_id]
823
        except KeyError:
380 by Martin Pool
- Slight optimization for Inventory.__getitem__
824
            if file_id == None:
825
                raise BzrError("can't look up file_id None")
826
            else:
827
                raise BzrError("file_id {%s} not in inventory" % file_id)
1 by mbp at sourcefrog
import from baz patch-364
828
829
460 by Martin Pool
- new testing command compare-trees
830
    def get_file_kind(self, file_id):
831
        return self._byid[file_id].kind
832
138 by mbp at sourcefrog
remove parallel tree from inventory;
833
    def get_child(self, parent_id, filename):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
834
        return self[parent_id].children.get(filename)
138 by mbp at sourcefrog
remove parallel tree from inventory;
835
836
1 by mbp at sourcefrog
import from baz patch-364
837
    def add(self, entry):
838
        """Add entry to inventory.
839
840
        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
841
        which calls this.
842
843
        Returns the new entry object.
844
        """
139 by mbp at sourcefrog
simplified/faster Inventory.add
845
        if entry.file_id in self._byid:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
846
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1 by mbp at sourcefrog
import from baz patch-364
847
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
848
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
849
            entry.parent_id = self.root.file_id
850
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
851
        try:
852
            parent = self._byid[entry.parent_id]
853
        except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
854
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
139 by mbp at sourcefrog
simplified/faster Inventory.add
855
856
        if parent.children.has_key(entry.name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
857
            raise BzrError("%s is already versioned" %
140 by mbp at sourcefrog
fix error message for repeated add
858
                    appendpath(self.id2path(parent.file_id), entry.name))
1 by mbp at sourcefrog
import from baz patch-364
859
860
        self._byid[entry.file_id] = entry
139 by mbp at sourcefrog
simplified/faster Inventory.add
861
        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
862
        return entry
1 by mbp at sourcefrog
import from baz patch-364
863
864
70 by mbp at sourcefrog
Prepare for smart recursive add.
865
    def add_path(self, relpath, kind, file_id=None):
866
        """Add entry from a path.
867
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
868
        The immediate parent must already be versioned.
869
870
        Returns the new entry object."""
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
871
        from bzrlib.branch import gen_file_id
1015 by Martin Pool
- fix circular imports
872
        
70 by mbp at sourcefrog
Prepare for smart recursive add.
873
        parts = bzrlib.osutils.splitpath(relpath)
874
        if len(parts) == 0:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
875
            raise BzrError("cannot re-add root of inventory")
70 by mbp at sourcefrog
Prepare for smart recursive add.
876
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
877
        if file_id == None:
800 by Martin Pool
Merge John's import-speedup branch:
878
            file_id = gen_file_id(relpath)
70 by mbp at sourcefrog
Prepare for smart recursive add.
879
753 by Martin Pool
- new exception NotVersionedError
880
        parent_path = parts[:-1]
881
        parent_id = self.path2id(parent_path)
882
        if parent_id == None:
883
            raise NotVersionedError(parent_path)
884
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
885
        if kind == 'directory':
886
            ie = InventoryDirectory(file_id, parts[-1], parent_id)
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
887
        elif kind == 'file':
888
            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
889
        elif kind == 'symlink':
890
            ie = InventoryLink(file_id, parts[-1], parent_id)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
891
        else:
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
892
            raise BzrError("unknown kind %r" % kind)
70 by mbp at sourcefrog
Prepare for smart recursive add.
893
        return self.add(ie)
894
895
1 by mbp at sourcefrog
import from baz patch-364
896
    def __delitem__(self, file_id):
897
        """Remove entry by id.
898
899
        >>> inv = Inventory()
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
900
        >>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
901
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
902
        >>> '123' in inv
903
        True
904
        >>> del inv['123']
905
        >>> '123' in inv
906
        False
907
        """
908
        ie = self[file_id]
909
138 by mbp at sourcefrog
remove parallel tree from inventory;
910
        assert self[ie.parent_id].children[ie.name] == ie
1 by mbp at sourcefrog
import from baz patch-364
911
        
912
        # TODO: Test deleting all children; maybe hoist to a separate
913
        # deltree method?
914
        if ie.kind == 'directory':
138 by mbp at sourcefrog
remove parallel tree from inventory;
915
            for cie in ie.children.values():
1 by mbp at sourcefrog
import from baz patch-364
916
                del self[cie.file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
917
            del ie.children
1 by mbp at sourcefrog
import from baz patch-364
918
919
        del self._byid[file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
920
        del self[ie.parent_id].children[ie.name]
1 by mbp at sourcefrog
import from baz patch-364
921
922
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
923
    def __eq__(self, other):
1 by mbp at sourcefrog
import from baz patch-364
924
        """Compare two sets by comparing their contents.
925
926
        >>> i1 = Inventory()
927
        >>> i2 = Inventory()
928
        >>> i1 == i2
929
        True
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
930
        >>> i1.add(InventoryFile('123', 'foo', ROOT_ID))
931
        InventoryFile('123', 'foo', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
932
        >>> i1 == i2
933
        False
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
934
        >>> i2.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
        True
938
        """
939
        if not isinstance(other, Inventory):
940
            return NotImplemented
941
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
942
        if len(self._byid) != len(other._byid):
543 by Martin Pool
- More cleanups for set type
943
            # shortcut: obviously not the same
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
944
            return False
945
946
        return self._byid == other._byid
947
948
949
    def __ne__(self, other):
1249 by Martin Pool
- improvements to weave commit [broken]
950
        return not self.__eq__(other)
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
951
952
953
    def __hash__(self):
954
        raise ValueError('not hashable')
955
1 by mbp at sourcefrog
import from baz patch-364
956
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
957
    def get_idpath(self, file_id):
958
        """Return a list of file_ids for the path to an entry.
959
960
        The list contains one element for each directory followed by
961
        the id of the file itself.  So the length of the returned list
962
        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
963
        root directory as depth 1.
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
964
        """
965
        p = []
966
        while file_id != None:
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
967
            try:
968
                ie = self._byid[file_id]
969
            except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
970
                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
971
            p.insert(0, ie.file_id)
972
            file_id = ie.parent_id
973
        return p
974
975
1 by mbp at sourcefrog
import from baz patch-364
976
    def id2path(self, file_id):
1393.1.32 by Martin Pool
- add docstring demonstrating use of Inventory.id2path
977
        """Return as a list the path to file_id.
978
        
979
        >>> i = Inventory()
980
        >>> e = i.add(InventoryDirectory('src-id', 'src', ROOT_ID))
981
        >>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
982
        >>> print i.id2path('foo-id').replace(os.sep, '/')
983
        src/foo.c
984
        """
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
985
        # get all names, skipping root
922 by Martin Pool
- optimization for Inventory.id2path; access byid map directly rather than
986
        p = [self._byid[fid].name for fid in self.get_idpath(file_id)[1:]]
271 by Martin Pool
- Windows path fixes
987
        return os.sep.join(p)
1 by mbp at sourcefrog
import from baz patch-364
988
            
989
990
991
    def path2id(self, name):
992
        """Walk down through directories to return entry of last component.
993
994
        names may be either a list of path components, or a single
995
        string, in which case it is automatically split.
996
997
        This returns the entry of the last component in the path,
998
        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
999
1000
        Returns None iff the path is not found.
1 by mbp at sourcefrog
import from baz patch-364
1001
        """
70 by mbp at sourcefrog
Prepare for smart recursive add.
1002
        if isinstance(name, types.StringTypes):
1003
            name = splitpath(name)
1 by mbp at sourcefrog
import from baz patch-364
1004
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
1005
        mutter("lookup path %r" % name)
1006
1007
        parent = self.root
70 by mbp at sourcefrog
Prepare for smart recursive add.
1008
        for f in name:
1 by mbp at sourcefrog
import from baz patch-364
1009
            try:
138 by mbp at sourcefrog
remove parallel tree from inventory;
1010
                cie = parent.children[f]
1 by mbp at sourcefrog
import from baz patch-364
1011
                assert cie.name == f
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
1012
                assert cie.parent_id == parent.file_id
138 by mbp at sourcefrog
remove parallel tree from inventory;
1013
                parent = cie
1 by mbp at sourcefrog
import from baz patch-364
1014
            except KeyError:
1015
                # or raise an error?
1016
                return None
1017
138 by mbp at sourcefrog
remove parallel tree from inventory;
1018
        return parent.file_id
1 by mbp at sourcefrog
import from baz patch-364
1019
1020
1021
    def has_filename(self, names):
1022
        return bool(self.path2id(names))
1023
1024
1025
    def has_id(self, file_id):
1026
        return self._byid.has_key(file_id)
1027
1028
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
1029
    def rename(self, file_id, new_parent_id, new_name):
1030
        """Move a file within the inventory.
1031
1032
        This can change either the name, or the parent, or both.
1033
1034
        This does not move the working file."""
1035
        if not is_valid_name(new_name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1036
            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
1037
1038
        new_parent = self._byid[new_parent_id]
1039
        if new_name in new_parent.children:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1040
            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
1041
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
1042
        new_parent_idpath = self.get_idpath(new_parent_id)
1043
        if file_id in new_parent_idpath:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
1044
            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
1045
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
1046
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
1047
        file_ie = self._byid[file_id]
1048
        old_parent = self._byid[file_ie.parent_id]
1049
1050
        # TODO: Don't leave things messed up if this fails
1051
1052
        del old_parent.children[file_ie.name]
1053
        new_parent.children[new_name] = file_ie
1054
        
1055
        file_ie.name = new_name
1056
        file_ie.parent_id = new_parent_id
1057
1058
1059
1060
1077 by Martin Pool
- avoid compiling REs at module load time
1061
_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
1062
1063
def is_valid_name(name):
1077 by Martin Pool
- avoid compiling REs at module load time
1064
    global _NAME_RE
1065
    if _NAME_RE == None:
1066
        _NAME_RE = re.compile(r'^[^/\\]+$')
1067
        
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
1068
    return bool(_NAME_RE.match(name))