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