~jelmer/bzr-git/705807-dpush

335 by Jelmer Vernooij
Move inventory code to separate module.
1
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
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
18
"""Git inventory."""
19
20
455 by Jelmer Vernooij
Add extra paranoia check.
21
from dulwich.objects import (
22
    Blob,
23
    Tree,
24
    )
25
26
335 by Jelmer Vernooij
Move inventory code to separate module.
27
from bzrlib import (
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
28
    errors,
335 by Jelmer Vernooij
Move inventory code to separate module.
29
    inventory,
30
    osutils,
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
31
    ui,
335 by Jelmer Vernooij
Move inventory code to separate module.
32
    )
33
521 by Jelmer Vernooij
Abstract out kind mapping a bit, initial work on support tree-references.
34
from bzrlib.plugins.git.mapping import (
35
    mode_kind,
36
    mode_is_executable,
37
    )
38
335 by Jelmer Vernooij
Move inventory code to separate module.
39
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
40
class GitInventoryEntry(inventory.InventoryEntry):
41
455 by Jelmer Vernooij
Add extra paranoia check.
42
    _git_class = None
43
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
44
    def __init__(self, inv, parent_id, hexsha, path, name, executable):
45
        self.name = name
46
        self.parent_id = parent_id
47
        self._inventory = inv
48
        self._object = None
49
        self.hexsha = hexsha
50
        self.path = path
51
        self.revision = self._inventory.revision_id
52
        self.executable = executable
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
53
        self.file_id = self._inventory.fileid_map.lookup_file_id(
710 by Jelmer Vernooij
Check types of file ids.
54
            path.encode('utf-8'))
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
55
56
    @property
57
    def object(self):
58
        if self._object is None:
59
            self._object = self._inventory.store[self.hexsha]
455 by Jelmer Vernooij
Add extra paranoia check.
60
            assert isinstance(self._object, self._git_class), \
61
                    "Expected instance of %r, got %r" % \
62
                    (self._git_class, self._object)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
63
        return self._object
64
65
66
class GitInventoryFile(GitInventoryEntry):
67
455 by Jelmer Vernooij
Add extra paranoia check.
68
    _git_class = Blob
69
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
70
    def __init__(self, inv, parent_id, hexsha, path, basename, executable):
695 by Jelmer Vernooij
Clean up trailing whitespace.
71
        super(GitInventoryFile, self).__init__(inv, parent_id, hexsha, path,
72
            basename, executable)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
73
        self.kind = 'file'
380 by Jelmer Vernooij
Support copy on inventory entries.
74
        self.text_id = None
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
75
        self.symlink_target = None
76
77
    @property
78
    def text_sha1(self):
851 by Jelmer Vernooij
Use blob.chunked.
79
        return osutils.sha_strings(self.object.chunked)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
80
81
    @property
82
    def text_size(self):
83
        return len(self.object.data)
84
397 by Jelmer Vernooij
Fix GitInventory.__repr__ to include more information.
85
    def __repr__(self):
86
        return ("%s(%r, %r, parent_id=%r, sha1=%r, len=%s, revision=%s)"
87
                % (self.__class__.__name__,
88
                   self.file_id,
89
                   self.name,
90
                   self.parent_id,
91
                   self.text_sha1,
92
                   self.text_size,
93
                   self.revision))
94
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
95
    def kind_character(self):
96
        """See InventoryEntry.kind_character."""
97
        return ''
98
380 by Jelmer Vernooij
Support copy on inventory entries.
99
    def copy(self):
695 by Jelmer Vernooij
Clean up trailing whitespace.
100
        other = inventory.InventoryFile(self.file_id, self.name,
101
            self.parent_id)
380 by Jelmer Vernooij
Support copy on inventory entries.
102
        other.executable = self.executable
103
        other.text_id = self.text_id
104
        other.text_sha1 = self.text_sha1
105
        other.text_size = self.text_size
106
        other.revision = self.revision
107
        return other
108
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
109
110
class GitInventoryLink(GitInventoryEntry):
111
455 by Jelmer Vernooij
Add extra paranoia check.
112
    _git_class = Blob
113
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
114
    def __init__(self, inv, parent_id, hexsha, path, basename, executable):
115
        super(GitInventoryLink, self).__init__(inv, parent_id, hexsha, path, basename, executable)
116
        self.text_sha1 = None
117
        self.text_size = None
118
        self.kind = 'symlink'
119
120
    @property
121
    def symlink_target(self):
122
        return self.object.data
123
124
    def kind_character(self):
125
        """See InventoryEntry.kind_character."""
126
        return ''
127
380 by Jelmer Vernooij
Support copy on inventory entries.
128
    def copy(self):
129
        other = inventory.InventoryLink(self.file_id, self.name, self.parent_id)
699.1.1 by INADA Naoki
Handle executable mode for symlink.
130
        other.executable = self.executable
380 by Jelmer Vernooij
Support copy on inventory entries.
131
        other.symlink_target = self.symlink_target
132
        other.revision = self.revision
133
        return other
134
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
135
521 by Jelmer Vernooij
Abstract out kind mapping a bit, initial work on support tree-references.
136
class GitInventoryTreeReference(GitInventoryEntry):
137
138
    _git_class = None
139
140
    def __init__(self, inv, parent_id, hexsha, path, basename, executable):
141
        super(GitInventoryTreeReference, self).__init__(inv, parent_id, hexsha, path, basename, executable)
664 by Jelmer Vernooij
Support submodules during fetch.
142
        self.hexsha = hexsha
143
        self.reference_revision = inv.mapping.revision_id_foreign_to_bzr(hexsha)
521 by Jelmer Vernooij
Abstract out kind mapping a bit, initial work on support tree-references.
144
        self.text_sha1 = None
145
        self.text_size = None
146
        self.symlink_target = None
147
        self.kind = 'tree-reference'
148
        self._children = None
149
150
    def kind_character(self):
151
        """See InventoryEntry.kind_character."""
152
        return '/'
153
154
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
155
class GitInventoryDirectory(GitInventoryEntry):
156
455 by Jelmer Vernooij
Add extra paranoia check.
157
    _git_class = Tree
158
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
159
    def __init__(self, inv, parent_id, hexsha, path, basename, executable):
160
        super(GitInventoryDirectory, self).__init__(inv, parent_id, hexsha, path, basename, executable)
161
        self.text_sha1 = None
162
        self.text_size = None
163
        self.symlink_target = None
164
        self.kind = 'directory'
403 by Jelmer Vernooij
Retrieve children faster.
165
        self._children = None
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
166
167
    def kind_character(self):
168
        """See InventoryEntry.kind_character."""
169
        return '/'
170
171
    @property
172
    def children(self):
403 by Jelmer Vernooij
Retrieve children faster.
173
        if self._children is None:
174
            self._retrieve_children()
175
        return self._children
176
177
    def _retrieve_children(self):
178
        self._children = {}
1147 by Jelmer Vernooij
Use Tree.items() rather than Tree.entries().
179
        for name, mode, hexsha in self.object.iteritems():
336 by Jelmer Vernooij
Use custom GitInventory class.
180
            basename = name.decode("utf-8")
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
181
            child_path = osutils.pathjoin(self.path, basename)
900.1.35 by Jelmer Vernooij
Ignore control files in inventories.
182
            if self._inventory.mapping.is_control_file(child_path):
183
                continue
521 by Jelmer Vernooij
Abstract out kind mapping a bit, initial work on support tree-references.
184
            executable = mode_is_executable(mode)
185
            kind_class = {'directory': GitInventoryDirectory,
186
                          'file': GitInventoryFile,
187
                          'symlink': GitInventoryLink,
188
                          'tree-reference': GitInventoryTreeReference}[mode_kind(mode)]
710 by Jelmer Vernooij
Check types of file ids.
189
            self._children[basename] = kind_class(self._inventory,
190
                self.file_id, hexsha, child_path, basename, executable)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
191
380 by Jelmer Vernooij
Support copy on inventory entries.
192
    def copy(self):
695 by Jelmer Vernooij
Clean up trailing whitespace.
193
        other = inventory.InventoryDirectory(self.file_id, self.name,
380 by Jelmer Vernooij
Support copy on inventory entries.
194
                                             self.parent_id)
195
        other.revision = self.revision
196
        # note that children are *not* copied; they're pulled across when
197
        # others are added
198
        return other
199
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
200
201
class GitInventory(inventory.Inventory):
202
982 by Jelmer Vernooij
Add __repr__ implementations for GitInventory and GitIndexInventory.
203
    def __repr__(self):
204
        return "<%s for %r in %r>" % (self.__class__.__name__,
205
                self.root.hexsha, self.store)
206
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
207
    def __init__(self, tree_id, mapping, fileid_map, store, revision_id):
381 by Jelmer Vernooij
Support working trees properly, status and ls.
208
        super(GitInventory, self).__init__(revision_id=revision_id)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
209
        self.store = store
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
210
        self.fileid_map = fileid_map
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
211
        self.mapping = mapping
397 by Jelmer Vernooij
Fix GitInventory.__repr__ to include more information.
212
        self.root = GitInventoryDirectory(self, None, tree_id, u"", u"", False)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
213
214
    def _get_ie(self, path):
982 by Jelmer Vernooij
Add __repr__ implementations for GitInventory and GitIndexInventory.
215
        if path == "" or path == []:
515 by Jelmer Vernooij
Support Inventory.path2id('').
216
            return self.root
982 by Jelmer Vernooij
Add __repr__ implementations for GitInventory and GitIndexInventory.
217
        if isinstance(path, basestring):
218
            parts = path.split("/")
219
        else:
220
            parts = path
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
221
        ie = self.root
222
        for name in parts:
695 by Jelmer Vernooij
Clean up trailing whitespace.
223
            ie = ie.children[name]
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
224
        return ie
225
226
    def has_filename(self, path):
227
        try:
228
            self._get_ie(path)
229
            return True
230
        except KeyError:
231
            return False
232
233
    def has_id(self, file_id):
234
        try:
235
            self.id2path(file_id)
236
            return True
237
        except errors.NoSuchId:
238
            return False
239
240
    def id2path(self, file_id):
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
241
        path = self.fileid_map.lookup_path(file_id)
337 by Jelmer Vernooij
Use CommonInventory to lazily evaluate trees.
242
        try:
243
            ie = self._get_ie(path)
244
        except KeyError:
245
            raise errors.NoSuchId(None, file_id)
246
247
    def path2id(self, path):
248
        try:
249
            return self._get_ie(path).file_id
250
        except KeyError:
251
            return None
252
253
    def __getitem__(self, file_id):
254
        if file_id == inventory.ROOT_ID:
255
            return self.root
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
256
        path = self.fileid_map.lookup_path(file_id)
381 by Jelmer Vernooij
Support working trees properly, status and ls.
257
        try:
258
            return self._get_ie(path)
259
        except KeyError:
260
            raise errors.NoSuchId(None, file_id)
401 by Jelmer Vernooij
Move working tree inventory code to inventory.
261
262
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
263
class GitIndexInventory(inventory.Inventory):
403 by Jelmer Vernooij
Retrieve children faster.
264
    """Inventory that retrieves its contents from an index file."""
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
265
982 by Jelmer Vernooij
Add __repr__ implementations for GitInventory and GitIndexInventory.
266
    def __repr__(self):
267
        return "<%s for %r>" % (self.__class__.__name__, self.index)
268
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
269
    def __init__(self, basis_inventory, fileid_map, index, store):
983 by Jelmer Vernooij
Use support opening index of first revision.
270
        if basis_inventory is None:
271
            root_id = None
272
        else:
273
            root_id = basis_inventory.root.file_id
274
        super(GitIndexInventory, self).__init__(revision_id=None, root_id=root_id)
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
275
        self.basis_inv = basis_inventory
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
276
        self.fileid_map = fileid_map
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
277
        self.index = index
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
278
        self._contents_read = False
710 by Jelmer Vernooij
Check types of file ids.
279
        self.store = store
695 by Jelmer Vernooij
Clean up trailing whitespace.
280
        self.root = self.add_path("", 'directory',
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
281
            self.fileid_map.lookup_file_id(""), None)
621 by Jelmer Vernooij
More complete inventory implementation.
282
283
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
284
        self._read_contents()
710 by Jelmer Vernooij
Check types of file ids.
285
        return super(GitIndexInventory, self).iter_entries_by_dir(
286
            specific_file_ids=specific_file_ids, yield_parents=yield_parents)
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
287
288
    def has_id(self, file_id):
710 by Jelmer Vernooij
Check types of file ids.
289
        if type(file_id) != str:
290
            raise AssertionError
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
291
        try:
292
            self.id2path(file_id)
293
            return True
294
        except errors.NoSuchId:
295
            return False
296
297
    def has_filename(self, path):
298
        if path in self.index:
299
            return True
300
        self._read_contents()
301
        return super(GitIndexInventory, self).has_filename(path)
302
303
    def id2path(self, file_id):
710 by Jelmer Vernooij
Check types of file ids.
304
        if type(file_id) != str:
305
            raise AssertionError
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
306
        path = self.fileid_map.lookup_path(file_id)
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
307
        if path in self.index:
308
            return path
309
        self._read_contents()
310
        return super(GitIndexInventory, self).id2path(file_id)
311
312
    def path2id(self, path):
982 by Jelmer Vernooij
Add __repr__ implementations for GitInventory and GitIndexInventory.
313
        if type(path) in (list, tuple):
314
            path = "/".join(path)
1115 by Jelmer Vernooij
Encode path before passing to lookup_file_id.
315
        encoded_path = path.encode("utf-8")
316
        if encoded_path in self.index:
317
            file_id = self.fileid_map.lookup_file_id(encoded_path)
974 by Jelmer Vernooij
Provide VersionedFiles.get_annotator.
318
        else:
319
            self._read_contents()
320
            file_id = super(GitIndexInventory, self).path2id(path)
990 by Jelmer Vernooij
Support 'add' in empty directory.
321
        if file_id is not None and type(file_id) is not str:
974 by Jelmer Vernooij
Provide VersionedFiles.get_annotator.
322
            raise AssertionError
323
        return file_id
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
324
325
    def __getitem__(self, file_id):
326
        self._read_contents()
327
        return super(GitIndexInventory, self).__getitem__(file_id)
328
329
    def _read_contents(self):
330
        if self._contents_read:
331
            return
332
        self._contents_read = True
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
333
        pb = ui.ui_factory.nested_progress_bar()
334
        try:
335
            for i, (path, value) in enumerate(self.index.iteritems()):
695 by Jelmer Vernooij
Clean up trailing whitespace.
336
                pb.update("creating working inventory from index",
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
337
                        i, len(self.index))
338
                assert isinstance(path, str)
339
                assert isinstance(value, tuple) and len(value) == 10
522 by Jelmer Vernooij
Simplify index handling a bit more.
340
                (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
983 by Jelmer Vernooij
Use support opening index of first revision.
341
                if self.basis_inv is not None:
342
                    try:
343
                        old_ie = self.basis_inv._get_ie(path)
344
                    except KeyError:
345
                        old_ie = None
346
                else:
403 by Jelmer Vernooij
Retrieve children faster.
347
                    old_ie = None
348
                if old_ie is None:
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
349
                    file_id = self.fileid_map.lookup_file_id(path)
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
350
                else:
403 by Jelmer Vernooij
Retrieve children faster.
351
                    file_id = old_ie.file_id
710 by Jelmer Vernooij
Check types of file ids.
352
                if type(file_id) != str:
353
                    raise AssertionError
522 by Jelmer Vernooij
Simplify index handling a bit more.
354
                kind = mode_kind(mode)
403 by Jelmer Vernooij
Retrieve children faster.
355
                if old_ie is not None and old_ie.hexsha == sha:
356
                    # Hasn't changed since basis inv
404 by Jelmer Vernooij
Actually put contents in the working tree inventory.
357
                    self.add_parents(path)
358
                    self.add(old_ie)
403 by Jelmer Vernooij
Retrieve children faster.
359
                else:
1110 by Jelmer Vernooij
Fix encoding handling in GitInventory.
360
                    ie = self.add_path(path.decode("utf-8"), kind, file_id,
695 by Jelmer Vernooij
Clean up trailing whitespace.
361
                        self.add_parents(path))
710 by Jelmer Vernooij
Check types of file ids.
362
                    data = self.store[sha].data
502 by Jelmer Vernooij
Fill in sha1s in inventory.
363
                    if kind == "symlink":
364
                        ie.symlink_target = data
365
                    else:
366
                        ie.text_sha1 = osutils.sha_string(data)
367
                        ie.text_size = len(data)
403 by Jelmer Vernooij
Retrieve children faster.
368
                    ie.revision = None
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
369
        finally:
370
            pb.finished()
371
372
    def add_parents(self, path):
1110 by Jelmer Vernooij
Fix encoding handling in GitInventory.
373
        assert isinstance(path, str)
401 by Jelmer Vernooij
Move working tree inventory code to inventory.
374
        dirname, _ = osutils.split(path)
620 by Jelmer Vernooij
Don't convert index to inventory unless we have to.
375
        file_id = super(GitIndexInventory, self).path2id(dirname)
401 by Jelmer Vernooij
Move working tree inventory code to inventory.
376
        if file_id is None:
377
            if dirname == "":
378
                parent_fid = None
379
            else:
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
380
                parent_fid = self.add_parents(dirname)
1110 by Jelmer Vernooij
Fix encoding handling in GitInventory.
381
            ie = self.add_path(dirname.decode("utf-8"), 'directory',
900.1.33 by Jelmer Vernooij
Fix file id map lookups.
382
                    self.fileid_map.lookup_file_id(dirname), parent_fid)
983 by Jelmer Vernooij
Use support opening index of first revision.
383
            if self.basis_inv is not None and ie.file_id in self.basis_inv:
402 by Jelmer Vernooij
Turn GitIndexInventory into an object, use progress bars.
384
                ie.revision = self.basis_inv[ie.file_id].revision
401 by Jelmer Vernooij
Move working tree inventory code to inventory.
385
            file_id = ie.file_id
710 by Jelmer Vernooij
Check types of file ids.
386
        if type(file_id) != str:
387
            raise AssertionError
401 by Jelmer Vernooij
Move working tree inventory code to inventory.
388
        return file_id
389