/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
1 by mbp at sourcefrog
import from baz patch-364
2
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.
7
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.
12
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
22
# TODO: Perhaps split InventoryEntry into subclasses for files,
23
# directories, etc etc.
24
25
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
26
# This should really be an id randomly assigned when the tree is
27
# created, but it's not for now.
28
ROOT_ID = "TREE_ROOT"
29
30
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
31
import os.path
32
import re
33
import sys
34
import types
1 by mbp at sourcefrog
import from baz patch-364
35
800 by Martin Pool
Merge John's import-speedup branch:
36
import bzrlib
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
37
from bzrlib.errors import BzrError, BzrCheckError
70 by mbp at sourcefrog
Prepare for smart recursive add.
38
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
39
from bzrlib.osutils import quotefn, splitpath, joinpath, appendpath, sha_strings
70 by mbp at sourcefrog
Prepare for smart recursive add.
40
from bzrlib.trace import mutter
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
41
from bzrlib.errors import NotVersionedError
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
42
1 by mbp at sourcefrog
import from baz patch-364
43
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
44
class InventoryEntry(object):
1 by mbp at sourcefrog
import from baz patch-364
45
    """Description of a versioned file.
46
47
    An InventoryEntry has the following fields, which are also
48
    present in the XML inventory-entry element:
49
1197 by Martin Pool
doc
50
    file_id
51
52
    name
53
        (within the parent directory)
54
55
    kind
1398 by Robert Collins
integrate in Gustavos x-bit patch
56
        'directory' or 'file' or 'symlink'
1197 by Martin Pool
doc
57
58
    parent_id
59
        file_id of the parent directory, or ROOT_ID
60
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
61
    revision
1398 by Robert Collins
integrate in Gustavos x-bit patch
62
        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
63
        introduced.
1197 by Martin Pool
doc
64
1398 by Robert Collins
integrate in Gustavos x-bit patch
65
    executable
66
        Indicates that this file should be executable on systems
67
        that support it.
68
1197 by Martin Pool
doc
69
    text_sha1
70
        sha-1 of the text of the file
71
        
72
    text_size
73
        size in bytes of the text of the file
74
        
75
    (reading a version 4 tree created a text_id field.)
1 by mbp at sourcefrog
import from baz patch-364
76
77
    >>> i = Inventory()
78
    >>> i.path2id('')
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
79
    'TREE_ROOT'
80
    >>> i.add(InventoryEntry('123', 'src', 'directory', ROOT_ID))
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
81
    InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT')
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
82
    >>> i.add(InventoryEntry('2323', 'hello.c', 'file', parent_id='123'))
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
83
    InventoryEntry('2323', 'hello.c', kind='file', parent_id='123')
1 by mbp at sourcefrog
import from baz patch-364
84
    >>> for j in i.iter_entries():
85
    ...   print j
86
    ... 
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
87
    ('src', InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT'))
1 by mbp at sourcefrog
import from baz patch-364
88
    ('src/hello.c', InventoryEntry('2323', 'hello.c', kind='file', parent_id='123'))
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
89
    >>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
1 by mbp at sourcefrog
import from baz patch-364
90
    Traceback (most recent call last):
91
    ...
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
92
    BzrError: inventory already contains entry with id {2323}
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
93
    >>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
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
94
    InventoryEntry('2324', 'bye.c', kind='file', parent_id='123')
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
95
    >>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
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
96
    InventoryEntry('2325', 'wibble', kind='directory', parent_id='123')
1 by mbp at sourcefrog
import from baz patch-364
97
    >>> i.path2id('src/wibble')
98
    '2325'
99
    >>> '2325' in i
100
    True
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
101
    >>> i.add(InventoryEntry('2326', 'wibble.c', 'file', '2325'))
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
102
    InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
1 by mbp at sourcefrog
import from baz patch-364
103
    >>> i['2326']
104
    InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
105
    >>> for path, entry in i.iter_entries():
106
    ...     print path.replace('\\\\', '/')     # for win32 os.sep
107
    ...     assert i.path2id(path)
1 by mbp at sourcefrog
import from baz patch-364
108
    ... 
109
    src
110
    src/bye.c
111
    src/hello.c
112
    src/wibble
113
    src/wibble/wibble.c
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
114
    >>> i.id2path('2326').replace('\\\\', '/')
1 by mbp at sourcefrog
import from baz patch-364
115
    'src/wibble/wibble.c'
116
    """
1197 by Martin Pool
doc
117
    
955 by Martin Pool
- use __slots__ on InventoryEntry; rather faster
118
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
1398 by Robert Collins
integrate in Gustavos x-bit patch
119
                 'text_id', 'parent_id', 'children', 'executable', 
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
120
                 'revision', 'symlink_target']
1185 by Martin Pool
- add xml round-trip test for revisions
121
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
122
    def _add_text_to_weave(self, new_lines, parents, weave_store):
123
        weave_store.add_text(self.file_id, self.revision, new_lines, parents)
124
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
125
    def __init__(self, file_id, name, kind, parent_id, text_id=None):
1 by mbp at sourcefrog
import from baz patch-364
126
        """Create an InventoryEntry
127
        
128
        The filename must be a single component, relative to the
129
        parent directory; it cannot be a whole path or relative name.
130
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
131
        >>> e = InventoryEntry('123', 'hello.c', 'file', ROOT_ID)
1 by mbp at sourcefrog
import from baz patch-364
132
        >>> e.name
133
        'hello.c'
134
        >>> e.file_id
135
        '123'
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
136
        >>> e = InventoryEntry('123', 'src/hello.c', 'file', ROOT_ID)
1 by mbp at sourcefrog
import from baz patch-364
137
        Traceback (most recent call last):
395 by Martin Pool
- fix error raised from invalid InventoryEntry name
138
        BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
1 by mbp at sourcefrog
import from baz patch-364
139
        """
1183 by Martin Pool
- implement version 5 xml storage, and tests
140
        assert isinstance(name, basestring), name
376 by Martin Pool
- fix slow invariant check when reading in InventoryEntry objects
141
        if '/' in name or '\\' in name:
142
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
1 by mbp at sourcefrog
import from baz patch-364
143
        
1398 by Robert Collins
integrate in Gustavos x-bit patch
144
        self.executable = False
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
145
        self.revision = None
955 by Martin Pool
- use __slots__ on InventoryEntry; rather faster
146
        self.text_sha1 = None
147
        self.text_size = None
1 by mbp at sourcefrog
import from baz patch-364
148
        self.file_id = file_id
149
        self.name = name
150
        self.kind = kind
151
        self.text_id = text_id
152
        self.parent_id = parent_id
1092.2.6 by Robert Collins
symlink support updated to work
153
        self.symlink_target = None
138 by mbp at sourcefrog
remove parallel tree from inventory;
154
        if kind == 'directory':
155
            self.children = {}
237 by mbp at sourcefrog
- Better assertions in InventoryEntry constructor
156
        elif kind == 'file':
157
            pass
1092.2.6 by Robert Collins
symlink support updated to work
158
        elif kind == 'symlink':
159
            pass
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
160
        else:
237 by mbp at sourcefrog
- Better assertions in InventoryEntry constructor
161
            raise BzrError("unhandled entry kind %r" % kind)
162
1092.2.6 by Robert Collins
symlink support updated to work
163
    def read_symlink_target(self, path):
164
        if self.kind == 'symlink':
165
            try:
166
                self.symlink_target = os.readlink(path)
167
            except OSError,e:
168
                raise BzrError("os.readlink error, %s" % e)
1 by mbp at sourcefrog
import from baz patch-364
169
156 by mbp at sourcefrog
new "directories" command
170
    def sorted_children(self):
171
        l = self.children.items()
172
        l.sort()
173
        return l
174
1399.1.1 by Robert Collins
move checks for versionability of file kinds into InventoryEntry
175
    @staticmethod
176
    def versionable_kind(kind):
177
        return kind in ('file', 'directory', 'symlink')
178
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
179
    def check(self, checker, rev_id, inv, tree):
180
        if self.parent_id != None:
181
            if not inv.has_id(self.parent_id):
182
                raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
183
                        % (self.parent_id, rev_id))
184
        if self.kind == 'file':
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
185
            revision = self.revision
186
            t = (self.file_id, revision)
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
187
            if t in checker.checked_texts:
188
                prev_sha = checker.checked_texts[t] 
189
                if prev_sha != self.text_sha1:
190
                    raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
191
                                        (self.file_id, rev_id))
192
                else:
193
                    checker.repeated_text_cnt += 1
194
                    return
195
            mutter('check version {%s} of {%s}', rev_id, self.file_id)
196
            file_lines = tree.get_file_lines(self.file_id)
197
            checker.checked_text_cnt += 1 
198
            if self.text_size != sum(map(len, file_lines)):
199
                raise BzrCheckError('text {%s} wrong size' % self.text_id)
200
            if self.text_sha1 != sha_strings(file_lines):
201
                raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
202
            checker.checked_texts[t] = self.text_sha1
203
        elif self.kind == 'directory':
204
            if self.text_sha1 != None or self.text_size != None or self.text_id != None:
205
                raise BzrCheckError('directory {%s} has text in revision {%s}'
206
                        % (self.file_id, rev_id))
207
        elif self.kind == 'root_directory':
208
            pass
209
        elif self.kind == 'symlink':
210
            if self.text_sha1 != None or self.text_size != None or self.text_id != None:
211
                raise BzrCheckError('symlink {%s} has text in revision {%s}'
212
                        % (self.file_id, rev_id))
213
            if self.symlink_target == None:
214
                raise BzrCheckError('symlink {%s} has no target in revision {%s}'
215
                        % (self.file_id, rev_id))
216
        else:
217
            raise BzrCheckError('unknown entry kind %r in revision {%s}' % 
218
                                (self.kind, rev_id))
219
156 by mbp at sourcefrog
new "directories" command
220
1 by mbp at sourcefrog
import from baz patch-364
221
    def copy(self):
222
        other = InventoryEntry(self.file_id, self.name, self.kind,
1189 by Martin Pool
- BROKEN: partial support for commit into weave
223
                               self.parent_id)
1398 by Robert Collins
integrate in Gustavos x-bit patch
224
        other.executable = self.executable
1189 by Martin Pool
- BROKEN: partial support for commit into weave
225
        other.text_id = self.text_id
1 by mbp at sourcefrog
import from baz patch-364
226
        other.text_sha1 = self.text_sha1
227
        other.text_size = self.text_size
1092.2.6 by Robert Collins
symlink support updated to work
228
        other.symlink_target = self.symlink_target
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
229
        other.revision = self.revision
490 by Martin Pool
doc
230
        # note that children are *not* copied; they're pulled across when
231
        # others are added
1 by mbp at sourcefrog
import from baz patch-364
232
        return other
233
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
234
    def _get_snapshot_change(self, previous_entries):
235
        if len(previous_entries) > 1:
236
            return 'merged'
237
        elif len(previous_entries) == 0:
238
            return 'added'
239
        else:
240
            return 'modified/renamed/reparented'
1 by mbp at sourcefrog
import from baz patch-364
241
242
    def __repr__(self):
243
        return ("%s(%r, %r, kind=%r, parent_id=%r)"
244
                % (self.__class__.__name__,
245
                   self.file_id,
246
                   self.name,
247
                   self.kind,
248
                   self.parent_id))
249
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
250
    def snapshot(self, revision, path, previous_entries, work_tree, 
251
                 weave_store):
252
        """Make a snapshot of this entry.
253
        
254
        This means that all its fields are populated, that it has its
255
        text stored in the text store or weave.
256
        """
257
        mutter('new parents of %s are %r', path, previous_entries)
258
        self._read_tree_state(path, work_tree)
259
        if len(previous_entries) == 1:
260
            # cannot be unchanged unless there is only one parent file rev.
261
            parent_ie = previous_entries.values()[0]
262
            if self._unchanged(path, parent_ie, work_tree):
263
                mutter("found unchanged entry")
264
                self.revision = parent_ie.revision
265
                return "unchanged"
266
        mutter('new revision for {%s}', self.file_id)
267
        self.revision = revision
268
        change = self._get_snapshot_change(previous_entries)
269
        if self.kind != 'file':
270
            return change
271
        self._snapshot_text(previous_entries, work_tree, weave_store)
272
        return change
273
274
    def _snapshot_text(self, file_parents, work_tree, weave_store): 
275
        mutter('storing file {%s} in revision {%s}',
276
               self.file_id, self.revision)
277
        # special case to avoid diffing on renames or 
278
        # reparenting
279
        if (len(file_parents) == 1
280
            and self.text_sha1 == file_parents.values()[0].text_sha1
281
            and self.text_size == file_parents.values()[0].text_size):
282
            previous_ie = file_parents.values()[0]
283
            weave_store.add_identical_text(
284
                self.file_id, previous_ie.revision, 
285
                self.revision, file_parents)
286
        else:
287
            new_lines = work_tree.get_file(self.file_id).readlines()
288
            self._add_text_to_weave(new_lines, file_parents, weave_store)
289
            self.text_sha1 = sha_strings(new_lines)
290
            self.text_size = sum(map(len, new_lines))
291
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
292
    def __eq__(self, other):
1 by mbp at sourcefrog
import from baz patch-364
293
        if not isinstance(other, InventoryEntry):
294
            return NotImplemented
295
1398 by Robert Collins
integrate in Gustavos x-bit patch
296
        return ((self.file_id == other.file_id)
297
                and (self.name == other.name)
298
                and (other.symlink_target == self.symlink_target)
299
                and (self.text_sha1 == other.text_sha1)
300
                and (self.text_size == other.text_size)
301
                and (self.text_id == other.text_id)
302
                and (self.parent_id == other.parent_id)
303
                and (self.kind == other.kind)
304
                and (self.revision == other.revision)
305
                and (self.executable == other.executable)
306
                )
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
307
308
    def __ne__(self, other):
309
        return not (self == other)
310
311
    def __hash__(self):
312
        raise ValueError('not hashable')
1 by mbp at sourcefrog
import from baz patch-364
313
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
314
    def _unchanged(self, path, previous_ie, work_tree):
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
315
        compatible = True
316
        # different inv parent
317
        if previous_ie.parent_id != self.parent_id:
318
            compatible = False
319
        # renamed
320
        elif previous_ie.name != self.name:
321
            compatible = False
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
322
        if self.kind == 'symlink':
323
            if self.symlink_target != previous_ie.symlink_target:
324
                compatible = False
325
        if self.kind == 'file':
326
            if self.text_sha1 != previous_ie.text_sha1:
327
                compatible = False
328
            else:
329
                # FIXME: 20050930 probe for the text size when getting sha1
330
                # in _read_tree_state
331
                self.text_size = previous_ie.text_size
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
332
        return compatible
333
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
334
    def _read_tree_state(self, path, work_tree):
335
        if self.kind == 'symlink':
336
            self.read_symlink_target(work_tree.abspath(path))
337
        if self.kind == 'file':
338
            self.text_sha1 = work_tree.get_file_sha1(self.file_id)
1398 by Robert Collins
integrate in Gustavos x-bit patch
339
            self.executable = work_tree.is_executable(self.file_id)
1092.2.23 by Robert Collins
move inventory entry centric snapshot taking logic to inventory.py
340
1 by mbp at sourcefrog
import from baz patch-364
341
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
342
class RootEntry(InventoryEntry):
343
    def __init__(self, file_id):
344
        self.file_id = file_id
345
        self.children = {}
346
        self.kind = 'root_directory'
347
        self.parent_id = None
156 by mbp at sourcefrog
new "directories" command
348
        self.name = ''
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
349
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
350
    def __eq__(self, other):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
351
        if not isinstance(other, RootEntry):
352
            return NotImplemented
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
353
        
354
        return (self.file_id == other.file_id) \
355
               and (self.children == other.children)
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
356
357
358
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
359
class Inventory(object):
1 by mbp at sourcefrog
import from baz patch-364
360
    """Inventory of versioned files in a tree.
361
240 by mbp at sourcefrog
doc
362
    This describes which file_id is present at each point in the tree,
363
    and possibly the SHA-1 or other information about the file.
364
    Entries can be looked up either by path or by file_id.
1 by mbp at sourcefrog
import from baz patch-364
365
366
    The inventory represents a typical unix file tree, with
367
    directories containing files and subdirectories.  We never store
368
    the full path to a file, because renaming a directory implicitly
369
    moves all of its contents.  This class internally maintains a
370
    lookup tree that allows the children under a directory to be
371
    returned quickly.
372
373
    InventoryEntry objects must not be modified after they are
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
374
    inserted, other than through the Inventory API.
1 by mbp at sourcefrog
import from baz patch-364
375
376
    >>> inv = Inventory()
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
377
    >>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
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
378
    InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
379
    >>> inv['123-123'].name
380
    'hello.c'
381
382
    May be treated as an iterator or set to look up file ids:
383
    
384
    >>> bool(inv.path2id('hello.c'))
385
    True
386
    >>> '123-123' in inv
387
    True
388
389
    May also look up by name:
390
391
    >>> [x[0] for x in inv.iter_entries()]
392
    ['hello.c']
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
393
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
394
    >>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
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
395
    InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT-12345678-12345678')
1 by mbp at sourcefrog
import from baz patch-364
396
    """
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
397
    def __init__(self, root_id=ROOT_ID):
1 by mbp at sourcefrog
import from baz patch-364
398
        """Create or read an inventory.
399
400
        If a working directory is specified, the inventory is read
401
        from there.  If the file is specified, read from that. If not,
402
        the inventory is created empty.
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
403
404
        The inventory is created with a default root directory, with
405
        an id of None.
1 by mbp at sourcefrog
import from baz patch-364
406
        """
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
407
        # 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
408
        # root id. Rather than generating a random one here.
409
        #if root_id is None:
410
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
411
        self.root = RootEntry(root_id)
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
412
        self._byid = {self.root.file_id: self.root}
1 by mbp at sourcefrog
import from baz patch-364
413
414
1189 by Martin Pool
- BROKEN: partial support for commit into weave
415
    def copy(self):
416
        other = Inventory(self.root.file_id)
1206 by Martin Pool
- fix bug in Inventory.copy()
417
        # copy recursively so we know directories will be added before
418
        # their children.  There are more efficient ways than this...
419
        for path, entry in self.iter_entries():
1189 by Martin Pool
- BROKEN: partial support for commit into weave
420
            if entry == self.root:
421
                continue
422
            other.add(entry.copy())
423
        return other
424
425
1 by mbp at sourcefrog
import from baz patch-364
426
    def __iter__(self):
427
        return iter(self._byid)
428
429
430
    def __len__(self):
431
        """Returns number of entries."""
432
        return len(self._byid)
433
434
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
435
    def iter_entries(self, from_dir=None):
1 by mbp at sourcefrog
import from baz patch-364
436
        """Return (path, entry) pairs, in order by name."""
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
437
        if from_dir == None:
438
            assert self.root
439
            from_dir = self.root
440
        elif isinstance(from_dir, basestring):
441
            from_dir = self._byid[from_dir]
442
            
443
        kids = from_dir.children.items()
1 by mbp at sourcefrog
import from baz patch-364
444
        kids.sort()
445
        for name, ie in kids:
446
            yield name, ie
447
            if ie.kind == 'directory':
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
448
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
271 by Martin Pool
- Windows path fixes
449
                    yield os.path.join(name, cn), cie
555 by Martin Pool
- New Inventory.entries() method
450
451
452
    def entries(self):
453
        """Return list of (path, ie) for all entries except the root.
454
455
        This may be faster than iter_entries.
456
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
457
        accum = []
458
        def descend(dir_ie, dir_path):
556 by Martin Pool
- fix up Inventory.entries()
459
            kids = dir_ie.children.items()
555 by Martin Pool
- New Inventory.entries() method
460
            kids.sort()
461
            for name, ie in kids:
462
                child_path = os.path.join(dir_path, name)
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
463
                accum.append((child_path, ie))
555 by Martin Pool
- New Inventory.entries() method
464
                if ie.kind == 'directory':
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
465
                    descend(ie, child_path)
555 by Martin Pool
- New Inventory.entries() method
466
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
467
        descend(self.root, '')
468
        return accum
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
469
470
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
471
    def directories(self):
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
472
        """Return (path, entry) pairs for all directories, including the root.
1 by mbp at sourcefrog
import from baz patch-364
473
        """
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
474
        accum = []
475
        def descend(parent_ie, parent_path):
476
            accum.append((parent_path, parent_ie))
156 by mbp at sourcefrog
new "directories" command
477
            
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
478
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
479
            kids.sort()
156 by mbp at sourcefrog
new "directories" command
480
557 by Martin Pool
- Refactor/cleanup Inventory.entries()
481
            for name, child_ie in kids:
482
                child_path = os.path.join(parent_path, name)
483
                descend(child_ie, child_path)
484
        descend(self.root, '')
485
        return accum
1 by mbp at sourcefrog
import from baz patch-364
486
        
487
488
489
    def __contains__(self, file_id):
490
        """True if this entry contains a file with given id.
491
492
        >>> inv = Inventory()
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
493
        >>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
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
494
        InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
495
        >>> '123' in inv
496
        True
497
        >>> '456' in inv
498
        False
499
        """
500
        return file_id in self._byid
501
502
503
    def __getitem__(self, file_id):
504
        """Return the entry for given file_id.
505
506
        >>> inv = Inventory()
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
507
        >>> inv.add(InventoryEntry('123123', 'hello.c', 'file', ROOT_ID))
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
508
        InventoryEntry('123123', 'hello.c', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
509
        >>> inv['123123'].name
510
        'hello.c'
511
        """
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
512
        try:
513
            return self._byid[file_id]
514
        except KeyError:
380 by Martin Pool
- Slight optimization for Inventory.__getitem__
515
            if file_id == None:
516
                raise BzrError("can't look up file_id None")
517
            else:
518
                raise BzrError("file_id {%s} not in inventory" % file_id)
1 by mbp at sourcefrog
import from baz patch-364
519
520
460 by Martin Pool
- new testing command compare-trees
521
    def get_file_kind(self, file_id):
522
        return self._byid[file_id].kind
523
138 by mbp at sourcefrog
remove parallel tree from inventory;
524
    def get_child(self, parent_id, filename):
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
525
        return self[parent_id].children.get(filename)
138 by mbp at sourcefrog
remove parallel tree from inventory;
526
527
1 by mbp at sourcefrog
import from baz patch-364
528
    def add(self, entry):
529
        """Add entry to inventory.
530
531
        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
532
        which calls this.
533
534
        Returns the new entry object.
535
        """
139 by mbp at sourcefrog
simplified/faster Inventory.add
536
        if entry.file_id in self._byid:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
537
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1 by mbp at sourcefrog
import from baz patch-364
538
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
539
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
540
            entry.parent_id = self.root.file_id
541
155 by mbp at sourcefrog
add new explicit RootEntry to inventory (in-core only)
542
        try:
543
            parent = self._byid[entry.parent_id]
544
        except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
545
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
139 by mbp at sourcefrog
simplified/faster Inventory.add
546
547
        if parent.children.has_key(entry.name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
548
            raise BzrError("%s is already versioned" %
140 by mbp at sourcefrog
fix error message for repeated add
549
                    appendpath(self.id2path(parent.file_id), entry.name))
1 by mbp at sourcefrog
import from baz patch-364
550
551
        self._byid[entry.file_id] = entry
139 by mbp at sourcefrog
simplified/faster Inventory.add
552
        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
553
        return entry
1 by mbp at sourcefrog
import from baz patch-364
554
555
70 by mbp at sourcefrog
Prepare for smart recursive add.
556
    def add_path(self, relpath, kind, file_id=None):
557
        """Add entry from a path.
558
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
559
        The immediate parent must already be versioned.
560
561
        Returns the new entry object."""
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
562
        from bzrlib.branch import gen_file_id
1015 by Martin Pool
- fix circular imports
563
        
70 by mbp at sourcefrog
Prepare for smart recursive add.
564
        parts = bzrlib.osutils.splitpath(relpath)
565
        if len(parts) == 0:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
566
            raise BzrError("cannot re-add root of inventory")
70 by mbp at sourcefrog
Prepare for smart recursive add.
567
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
568
        if file_id == None:
800 by Martin Pool
Merge John's import-speedup branch:
569
            file_id = gen_file_id(relpath)
70 by mbp at sourcefrog
Prepare for smart recursive add.
570
753 by Martin Pool
- new exception NotVersionedError
571
        parent_path = parts[:-1]
572
        parent_id = self.path2id(parent_path)
573
        if parent_id == None:
574
            raise NotVersionedError(parent_path)
575
70 by mbp at sourcefrog
Prepare for smart recursive add.
576
        ie = InventoryEntry(file_id, parts[-1],
577
                            kind=kind, parent_id=parent_id)
578
        return self.add(ie)
579
580
1 by mbp at sourcefrog
import from baz patch-364
581
    def __delitem__(self, file_id):
582
        """Remove entry by id.
583
584
        >>> inv = Inventory()
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
585
        >>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
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
586
        InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
587
        >>> '123' in inv
588
        True
589
        >>> del inv['123']
590
        >>> '123' in inv
591
        False
592
        """
593
        ie = self[file_id]
594
138 by mbp at sourcefrog
remove parallel tree from inventory;
595
        assert self[ie.parent_id].children[ie.name] == ie
1 by mbp at sourcefrog
import from baz patch-364
596
        
597
        # TODO: Test deleting all children; maybe hoist to a separate
598
        # deltree method?
599
        if ie.kind == 'directory':
138 by mbp at sourcefrog
remove parallel tree from inventory;
600
            for cie in ie.children.values():
1 by mbp at sourcefrog
import from baz patch-364
601
                del self[cie.file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
602
            del ie.children
1 by mbp at sourcefrog
import from baz patch-364
603
604
        del self._byid[file_id]
138 by mbp at sourcefrog
remove parallel tree from inventory;
605
        del self[ie.parent_id].children[ie.name]
1 by mbp at sourcefrog
import from baz patch-364
606
607
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
608
    def __eq__(self, other):
1 by mbp at sourcefrog
import from baz patch-364
609
        """Compare two sets by comparing their contents.
610
611
        >>> i1 = Inventory()
612
        >>> i2 = Inventory()
613
        >>> i1 == i2
614
        True
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
615
        >>> i1.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
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
616
        InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
617
        >>> i1 == i2
618
        False
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
619
        >>> i2.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
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
620
        InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
1 by mbp at sourcefrog
import from baz patch-364
621
        >>> i1 == i2
622
        True
623
        """
624
        if not isinstance(other, Inventory):
625
            return NotImplemented
626
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
627
        if len(self._byid) != len(other._byid):
543 by Martin Pool
- More cleanups for set type
628
            # shortcut: obviously not the same
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
629
            return False
630
631
        return self._byid == other._byid
632
633
634
    def __ne__(self, other):
1249 by Martin Pool
- improvements to weave commit [broken]
635
        return not self.__eq__(other)
544 by Martin Pool
- Define __eq__ and __ne__ for Inventory and InventoryEntry objects,
636
637
638
    def __hash__(self):
639
        raise ValueError('not hashable')
640
1 by mbp at sourcefrog
import from baz patch-364
641
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
642
    def get_idpath(self, file_id):
643
        """Return a list of file_ids for the path to an entry.
644
645
        The list contains one element for each directory followed by
646
        the id of the file itself.  So the length of the returned list
647
        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
648
        root directory as depth 1.
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
649
        """
650
        p = []
651
        while file_id != None:
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
652
            try:
653
                ie = self._byid[file_id]
654
            except KeyError:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
655
                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
656
            p.insert(0, ie.file_id)
657
            file_id = ie.parent_id
658
        return p
659
660
1 by mbp at sourcefrog
import from baz patch-364
661
    def id2path(self, file_id):
662
        """Return as a list the path to file_id."""
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
663
664
        # get all names, skipping root
922 by Martin Pool
- optimization for Inventory.id2path; access byid map directly rather than
665
        p = [self._byid[fid].name for fid in self.get_idpath(file_id)[1:]]
271 by Martin Pool
- Windows path fixes
666
        return os.sep.join(p)
1 by mbp at sourcefrog
import from baz patch-364
667
            
668
669
670
    def path2id(self, name):
671
        """Walk down through directories to return entry of last component.
672
673
        names may be either a list of path components, or a single
674
        string, in which case it is automatically split.
675
676
        This returns the entry of the last component in the path,
677
        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
678
679
        Returns None iff the path is not found.
1 by mbp at sourcefrog
import from baz patch-364
680
        """
70 by mbp at sourcefrog
Prepare for smart recursive add.
681
        if isinstance(name, types.StringTypes):
682
            name = splitpath(name)
1 by mbp at sourcefrog
import from baz patch-364
683
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
684
        mutter("lookup path %r" % name)
685
686
        parent = self.root
70 by mbp at sourcefrog
Prepare for smart recursive add.
687
        for f in name:
1 by mbp at sourcefrog
import from baz patch-364
688
            try:
138 by mbp at sourcefrog
remove parallel tree from inventory;
689
                cie = parent.children[f]
1 by mbp at sourcefrog
import from baz patch-364
690
                assert cie.name == f
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
691
                assert cie.parent_id == parent.file_id
138 by mbp at sourcefrog
remove parallel tree from inventory;
692
                parent = cie
1 by mbp at sourcefrog
import from baz patch-364
693
            except KeyError:
694
                # or raise an error?
695
                return None
696
138 by mbp at sourcefrog
remove parallel tree from inventory;
697
        return parent.file_id
1 by mbp at sourcefrog
import from baz patch-364
698
699
700
    def has_filename(self, names):
701
        return bool(self.path2id(names))
702
703
704
    def has_id(self, file_id):
705
        return self._byid.has_key(file_id)
706
707
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
708
    def rename(self, file_id, new_parent_id, new_name):
709
        """Move a file within the inventory.
710
711
        This can change either the name, or the parent, or both.
712
713
        This does not move the working file."""
714
        if not is_valid_name(new_name):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
715
            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
716
717
        new_parent = self._byid[new_parent_id]
718
        if new_name in new_parent.children:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
719
            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
720
172 by mbp at sourcefrog
- clearer check against attempts to introduce directory loops in the inventory
721
        new_parent_idpath = self.get_idpath(new_parent_id)
722
        if file_id in new_parent_idpath:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
723
            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
724
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
725
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
726
        file_ie = self._byid[file_id]
727
        old_parent = self._byid[file_ie.parent_id]
728
729
        # TODO: Don't leave things messed up if this fails
730
731
        del old_parent.children[file_ie.name]
732
        new_parent.children[new_name] = file_ie
733
        
734
        file_ie.name = new_name
735
        file_ie.parent_id = new_parent_id
736
737
738
739
1077 by Martin Pool
- avoid compiling REs at module load time
740
_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
741
742
def is_valid_name(name):
1077 by Martin Pool
- avoid compiling REs at module load time
743
    global _NAME_RE
744
    if _NAME_RE == None:
745
        _NAME_RE = re.compile(r'^[^/\\]+$')
746
        
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
747
    return bool(_NAME_RE.match(name))