/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
1
# Copyright (C) 2008 Canonical Ltd
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
"""CommitHandlers that build and save revisions & their inventories."""
18
19
20
from bzrlib import (
21
    errors,
22
    generate_ids,
23
    inventory,
24
    osutils,
25
    revision,
26
    )
27
from bzrlib.plugins.fastimport import helpers, processor
28
29
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
30
def copy_inventory(inv):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
31
    # This currently breaks revision-id matching
32
    #if hasattr(inv, "_get_mutable_inventory"):
33
    #    # TODO: Make this a public API on inventory
34
    #    return inv._get_mutable_inventory()
35
36
    # TODO: Shallow copy - deep inventory copying is expensive
37
    return inv.copy()
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
38
39
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
40
class GenericCommitHandler(processor.CommitHandler):
41
    """Base class for Bazaar CommitHandlers."""
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
42
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
43
    def __init__(self, command, cache_mgr, rev_store, verbose=False):
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
44
        super(GenericCommitHandler, self).__init__(command)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
45
        self.cache_mgr = cache_mgr
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
46
        self.rev_store = rev_store
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
47
        self.verbose = verbose
48
49
    def pre_process_files(self):
50
        """Prepare for committing."""
51
        self.revision_id = self.gen_revision_id()
52
        # cache of texts for this commit, indexed by file-id
53
        self.lines_for_commit = {}
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
54
        if self.rev_store.expects_rich_root():
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
55
            self.lines_for_commit[inventory.ROOT_ID] = []
56
57
        # Track the heads and get the real parent list
58
        parents = self.cache_mgr.track_heads(self.command)
59
60
        # Convert the parent commit-ids to bzr revision-ids
61
        if parents:
62
            self.parents = [self.cache_mgr.revision_ids[p]
63
                for p in parents]
64
        else:
65
            self.parents = []
66
        self.debug("%s id: %s, parents: %s", self.command.id,
67
            self.revision_id, str(self.parents))
68
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
69
        # Keep the basis inventory. This needs to be treated as read-only.
70
        if len(self.parents) == 0:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
71
            self.basis_inventory = self._init_inventory()
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
72
        else:
73
            self.basis_inventory = self.get_inventory(self.parents[0])
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
74
        if hasattr(self.basis_inventory, "root_id"):
75
            self.inventory_root_id = self.basis_inventory.root_id
76
        else:
77
            self.inventory_root_id = self.basis_inventory.root.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
78
79
        # directory-path -> inventory-entry for current inventory
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
80
        self.directory_entries = {}
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
81
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
82
    def _init_inventory(self):
83
        return self.rev_store.init_inventory(self.revision_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
84
85
    def get_inventory(self, revision_id):
86
        """Get the inventory for a revision id."""
87
        try:
88
            inv = self.cache_mgr.inventories[revision_id]
89
        except KeyError:
90
            if self.verbose:
91
                self.note("get_inventory cache miss for %s", revision_id)
92
            # Not cached so reconstruct from the RevisionStore
93
            inv = self.rev_store.get_inventory(revision_id)
94
            self.cache_mgr.inventories[revision_id] = inv
95
        return inv
96
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
97
    def _get_lines(self, file_id):
98
        """Get the lines for a file-id."""
99
        return self.lines_for_commit[file_id]
100
101
    def _get_inventories(self, revision_ids):
102
        """Get the inventories for revision-ids.
103
        
104
        This is a callback used by the RepositoryStore to
105
        speed up inventory reconstruction.
106
        """
107
        present = []
108
        inventories = []
109
        # If an inventory is in the cache, we assume it was
110
        # successfully loaded into the revision store
111
        for revision_id in revision_ids:
112
            try:
113
                inv = self.cache_mgr.inventories[revision_id]
114
                present.append(revision_id)
115
            except KeyError:
116
                if self.verbose:
117
                    self.note("get_inventories cache miss for %s", revision_id)
118
                # Not cached so reconstruct from the revision store
119
                try:
120
                    inv = self.get_inventory(revision_id)
121
                    present.append(revision_id)
122
                except:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
123
                    inv = self._init_inventory()
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
124
                self.cache_mgr.inventories[revision_id] = inv
125
            inventories.append(inv)
126
        return present, inventories
127
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
128
    def bzr_file_id_and_new(self, path):
129
        """Get a Bazaar file identifier and new flag for a path.
130
        
131
        :return: file_id, is_new where
132
          is_new = True if the file_id is newly created
133
        """
134
        try:
135
            id = self.cache_mgr.file_ids[path]
136
            return id, False
137
        except KeyError:
138
            id = generate_ids.gen_file_id(path)
139
            self.cache_mgr.file_ids[path] = id
140
            self.debug("Generated new file id %s for '%s'", id, path)
141
            return id, True
142
143
    def bzr_file_id(self, path):
144
        """Get a Bazaar file identifier for a path."""
145
        return self.bzr_file_id_and_new(path)[0]
146
147
    def gen_revision_id(self):
148
        """Generate a revision id.
149
150
        Subclasses may override this to produce deterministic ids say.
151
        """
152
        committer = self.command.committer
153
        # Perhaps 'who' being the person running the import is ok? If so,
154
        # it might be a bit quicker and give slightly better compression?
155
        who = "%s <%s>" % (committer[0],committer[1])
156
        timestamp = committer[2]
157
        return generate_ids.gen_revision_id(who, timestamp)
158
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
159
    def build_revision(self):
160
        rev_props = {}
161
        committer = self.command.committer
162
        who = "%s <%s>" % (committer[0],committer[1])
163
        author = self.command.author
164
        if author is not None:
165
            author_id = "%s <%s>" % (author[0],author[1])
166
            if author_id != who:
167
                rev_props['author'] = author_id
168
        return revision.Revision(
169
           timestamp=committer[2],
170
           timezone=committer[3],
171
           committer=who,
172
           message=helpers.escape_commit_message(self.command.message),
173
           revision_id=self.revision_id,
174
           properties=rev_props,
175
           parent_ids=self.parents)
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
176
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
177
    def _modify_item(self, path, kind, is_executable, data, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
178
        """Add to or change an item in the inventory."""
179
        # Create the new InventoryEntry
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
180
        basename, parent_id = self._ensure_directory(path, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
181
        file_id = self.bzr_file_id(path)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
182
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
183
        ie.revision = self.revision_id
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
184
        if kind == 'file':
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
185
            ie.executable = is_executable
186
            lines = osutils.split_lines(data)
187
            ie.text_sha1 = osutils.sha_strings(lines)
188
            ie.text_size = sum(map(len, lines))
189
            self.lines_for_commit[file_id] = lines
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
190
        elif kind == 'symlink':
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
191
            ie.symlink_target = data.encode('utf8')
192
            # There are no lines stored for a symlink so
193
            # make sure the cache used by get_lines knows that
194
            self.lines_for_commit[file_id] = []
195
        else:
196
            raise errors.BzrError("Cannot import items of kind '%s' yet" %
197
                (kind,))
198
        # Record it
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
199
        if file_id in inv:
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
200
            old_ie = inv[file_id]
201
            if old_ie.kind == 'directory':
202
                self.record_delete(path, old_ie)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
203
            self.record_changed(path, ie, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
204
        else:
205
            self.record_new(path, ie)
206
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
207
    def _ensure_directory(self, path, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
208
        """Ensure that the containing directory exists for 'path'"""
209
        dirname, basename = osutils.split(path)
210
        if dirname == '':
211
            # the root node doesn't get updated
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
212
            return basename, self.inventory_root_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
213
        try:
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
214
            ie = self._get_directory_entry(inv, dirname)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
215
        except KeyError:
216
            # We will create this entry, since it doesn't exist
217
            pass
218
        else:
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
219
            return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
220
221
        # No directory existed, we will just create one, first, make sure
222
        # the parent exists
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
223
        dir_basename, parent_id = self._ensure_directory(dirname, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
224
        dir_file_id = self.bzr_file_id(dirname)
225
        ie = inventory.entry_factory['directory'](dir_file_id,
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
226
            dir_basename, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
227
        ie.revision = self.revision_id
228
        self.directory_entries[dirname] = ie
229
        # There are no lines stored for a directory so
230
        # make sure the cache used by get_lines knows that
231
        self.lines_for_commit[dir_file_id] = []
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
232
233
        # It's possible that a file or symlink with that file-id
234
        # already exists. If it does, we need to delete it.
235
        if dir_file_id in inv:
236
            self.record_delete(dirname, ie)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
237
        self.record_new(dirname, ie)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
238
        return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
239
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
240
    def _get_directory_entry(self, inv, dirname):
241
        """Get the inventory entry for a directory.
242
        
243
        Raises KeyError if dirname is not a directory in inv.
244
        """
245
        result = self.directory_entries.get(dirname)
246
        if result is None:
247
            file_id = inv.path2id(dirname)
248
            if file_id is None:
249
                raise KeyError
250
            result = inv[file_id]
251
            # dirname must be a directory for us to return it
252
            if result.kind == 'directory':
253
                self.directory_entries[dirname] = result
254
            else:
255
                raise KeyError
256
        return result
257
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
258
    def _delete_item(self, path, inv):
259
        file_id = inv.path2id(path)
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
260
        try:
261
            ie = inv[file_id]
262
        except errors.NoSuchId:
263
            self.warning("ignoring delete of %s as not in inventory", path)
264
        else:
265
            self.record_delete(path, ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
266
267
    def _copy_item(self, src_path, dest_path, inv):
268
        if not self.parents:
269
            self.warning("ignoring copy of %s to %s - no parent revisions",
270
                src_path, dest_path)
271
            return
272
        file_id = inv.path2id(src_path)
273
        if file_id is None:
274
            self.warning("ignoring copy of %s to %s - source does not exist",
275
                src_path, dest_path)
276
            return
277
        ie = inv[file_id]
278
        kind = ie.kind
279
        if kind == 'file':
280
            content = self.rev_store.get_file_text(self.parents[0], file_id)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
281
            self._modify_item(dest_path, kind, ie.executable, content, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
282
        elif kind == 'symlink':
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
283
            self._modify_item(dest_path, kind, False, ie.symlink_target, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
284
        else:
285
            self.warning("ignoring copy of %s %s - feature not yet supported",
286
                kind, path)
287
288
    def _rename_item(self, old_path, new_path, inv):
0.81.8 by Ian Clatworthy
refactor rename_item
289
        file_id = inv.path2id(old_path)
290
        ie = inv[file_id]
291
        rev_id = ie.revision
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
292
        new_file_id = inv.path2id(new_path)
293
        if new_file_id is not None:
0.81.9 by Ian Clatworthy
refactor delete_item
294
            self.record_delete(new_path, inv[new_file_id])
0.81.8 by Ian Clatworthy
refactor rename_item
295
        self.record_rename(old_path, new_path, file_id, ie)
296
        self.cache_mgr.rename_path(old_path, new_path)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
297
0.81.8 by Ian Clatworthy
refactor rename_item
298
        # The revision-id for this entry will be/has been updated and
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
299
        # that means the loader then needs to know what the "new" text is.
300
        # We therefore must go back to the revision store to get it.
0.81.8 by Ian Clatworthy
refactor rename_item
301
        lines = self.rev_store.get_file_lines(rev_id, file_id)
302
        self.lines_for_commit[file_id] = lines
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
303
304
    def _delete_all_items(self, inv):
305
        for name, root_item in inv.root.children.iteritems():
306
            inv.remove_recursive_id(root_item.file_id)
307
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
308
    def _warn_unless_in_merges(self, fileid, path):
309
        if len(self.parents) <= 1:
310
            return
311
        for parent in self.parents[1:]:
312
            if fileid in self.get_inventory(parent):
313
                return
314
        self.warning("ignoring delete of %s as not in parent inventories", path)
315
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
316
317
class InventoryCommitHandler(GenericCommitHandler):
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
318
    """A CommitHandler that builds and saves Inventory objects."""
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
319
320
    def pre_process_files(self):
321
        super(InventoryCommitHandler, self).pre_process_files()
322
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
323
        # Seed the inventory from the previous one
324
        if len(self.parents) == 0:
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
325
            self.inventory = self.basis_inventory
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
326
        else:
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
327
            self.inventory = copy_inventory(self.basis_inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
328
        self.inventory_root = self.inventory.root
329
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
330
        # directory-path -> inventory-entry for current inventory
331
        self.directory_entries = dict(self.inventory.directories())
332
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
333
        # Initialise the inventory revision info as required
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
334
        if self.rev_store.expects_rich_root():
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
335
            self.inventory.revision_id = self.revision_id
336
        else:
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
337
            # In this revision store, root entries have no knit or weave.
338
            # When serializing out to disk and back in, root.revision is
339
            # always the new revision_id.
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
340
            self.inventory.root.revision = self.revision_id
341
342
    def post_process_files(self):
343
        """Save the revision."""
344
        self.cache_mgr.inventories[self.revision_id] = self.inventory
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
345
        rev = self.build_revision()
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
346
        self.rev_store.load(rev, self.inventory, None,
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
347
            lambda file_id: self._get_lines(file_id),
348
            lambda revision_ids: self._get_inventories(revision_ids))
349
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
350
    def record_new(self, path, ie):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
351
        try:
352
            self.inventory.add(ie)
353
        except errors.DuplicateFileId:
354
            # Directory already exists as a file or symlink
355
            del self.inventory[ie.file_id]
356
            # Try again
357
            self.inventory.add(ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
358
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
359
    def record_changed(self, path, ie, parent_id):
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
360
        # HACK: no API for this (del+add does more than it needs to)
361
        self.inventory._byid[ie.file_id] = ie
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
362
        parent_ie = self.inventory._byid[parent_id]
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
363
        parent_ie.children[ie.name] = ie
364
0.81.9 by Ian Clatworthy
refactor delete_item
365
    def record_delete(self, path, ie):
366
        self.inventory.remove_recursive_id(ie.file_id)
0.81.8 by Ian Clatworthy
refactor rename_item
367
368
    def record_rename(self, old_path, new_path, file_id, ie):
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
369
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
370
            self.inventory)
0.81.8 by Ian Clatworthy
refactor rename_item
371
        self.inventory.rename(file_id, new_parent_id, new_basename)
372
        self.inventory[file_id].revision = self.revision_id
373
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
374
    def _delete_item(self, path, inv):
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
375
        # NOTE: I'm retaining this method for now, instead of using the
376
        # one in the superclass, because it's taken quite a lot of tweaking
377
        # to cover all the edge cases seen in the wild. Long term, it can
378
        # probably go once the higher level method does "warn_unless_in_merges"
379
        # and handles all the various special cases ...
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
380
        fileid = self.bzr_file_id(path)
381
        dirname, basename = osutils.split(path)
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
382
        if (fileid in inv and
383
            isinstance(inv[fileid], inventory.InventoryDirectory)):
384
            for child_path in inv[fileid].children.keys():
385
                self._delete_item(osutils.pathjoin(path, child_path), inv)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
386
        try:
387
            if self.inventory.id2path(fileid) == path:
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
388
                del inv[fileid]
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
389
            else:
390
                # already added by some other name?
391
                if dirname in self.cache_mgr.file_ids:
392
                    parent_id = self.cache_mgr.file_ids[dirname]
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
393
                    del inv[parent_id].children[basename]
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
394
        except KeyError:
395
            self._warn_unless_in_merges(fileid, path)
396
        except errors.NoSuchId:
397
            self._warn_unless_in_merges(fileid, path)
398
        except AttributeError, ex:
399
            if ex.args[0] == 'children':
400
                # A directory has changed into a file and then one
401
                # of it's children is being deleted!
402
                self._warn_unless_in_merges(fileid, path)
403
            else:
404
                raise
405
        try:
406
            self.cache_mgr.delete_path(path)
407
        except KeyError:
408
            pass
409
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
410
    def modify_handler(self, filecmd):
411
        if filecmd.dataref is not None:
412
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
413
        else:
414
            data = filecmd.data
415
        self.debug("modifying %s", filecmd.path)
416
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
417
            filecmd.is_executable, data, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
418
419
    def delete_handler(self, filecmd):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
420
        self.debug("deleting %s", filecmd.path)
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
421
        self._delete_item(filecmd.path, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
422
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
423
    def copy_handler(self, filecmd):
424
        src_path = filecmd.src_path
425
        dest_path = filecmd.dest_path
426
        self.debug("copying %s to %s", src_path, dest_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
427
        self._copy_item(src_path, dest_path, self.inventory)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
428
429
    def rename_handler(self, filecmd):
430
        old_path = filecmd.old_path
431
        new_path = filecmd.new_path
432
        self.debug("renaming %s to %s", old_path, new_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
433
        self._rename_item(old_path, new_path, self.inventory)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
434
435
    def deleteall_handler(self, filecmd):
436
        self.debug("deleting all files (and also all directories)")
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
437
        self._delete_all_items(self.inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
438
439
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
440
class CHKInventoryCommitHandler(GenericCommitHandler):
441
    """A CommitHandler that builds and saves CHKInventory objects."""
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
442
443
    def pre_process_files(self):
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
444
        super(CHKInventoryCommitHandler, self).pre_process_files()
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
445
        # A given file-id can only appear once so we accumulate
446
        # the entries in a dict then build the actual delta at the end
447
        self._delta_entries_by_fileid = {}
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
448
        if len(self.parents) == 0 or not self.rev_store.expects_rich_root():
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
449
            if self.parents:
450
                old_path = ''
451
            else:
452
                old_path = None
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
453
            # Need to explicitly add the root entry for the first revision
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
454
            # and for non rich-root inventories
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
455
            root_id = inventory.ROOT_ID
456
            # XXX: We *could* make this a CHKInventoryDirectory but it
457
            # seems that deltas ought to use normal InventoryDirectory's
458
            # because they simply don't know the chk_inventory that they
459
            # are about to become a part of.
460
            root_ie = inventory.InventoryDirectory(root_id, u'', None)
461
            root_ie.revision = self.revision_id
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
462
            self._add_entry((old_path, '', root_id, root_ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
463
464
    def post_process_files(self):
465
        """Save the revision."""
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
466
        delta = list(self._delta_entries_by_fileid.values())
467
        #print "delta:\n%s\n\n" % "\n".join([str(de) for de in delta])
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
468
        rev = self.build_revision()
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
469
        inv = self.rev_store.chk_load(rev, self.basis_inventory, delta, None,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
470
            lambda file_id: self._get_lines(file_id),
471
            lambda revision_ids: self._get_inventories(revision_ids))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
472
        self.cache_mgr.inventories[self.revision_id] = inv
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
473
        #print "committed %s" % self.revision_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
474
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
475
    def _add_entry(self, entry):
476
        # We need to combine the data if multiple entries have the same fileid.
477
        # For example, a rename followed by a modification looks like:
478
        #
479
        # (x, y, f, e) & (y, y, f, g) => (x, y, f, g)
480
        #
481
        # Likewise, a modification followed by a rename looks like:
482
        #
483
        # (x, x, f, e) & (x, y, f, g) => (x, y, f, g)
484
        #
485
        # Here's a rename followed by a delete and a modification followed by
486
        # a delete:
487
        #
488
        # (x, y, f, e) & (y, None, f, None) => (x, None, f, None)
489
        # (x, x, f, e) & (x, None, f, None) => (x, None, f, None)
490
        #
491
        # In summary, we use the original old-path, new new-path and new ie
492
        # when combining entries.
493
        file_id = entry[2]
494
        existing = self._delta_entries_by_fileid.get(file_id, None)
495
        if existing is not None:
496
            entry = (existing[0], entry[1], file_id, entry[3])
497
        self._delta_entries_by_fileid[file_id] = entry
498
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
499
    def record_new(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
500
        self._add_entry((None, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
501
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
502
    def record_changed(self, path, ie, parent_id=None):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
503
        self._add_entry((path, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
504
0.81.9 by Ian Clatworthy
refactor delete_item
505
    def record_delete(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
506
        self._add_entry((path, None, ie.file_id, None))
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
507
        if ie.kind == 'directory':
508
            for child_path, entry in \
509
                self.basis_inventory.iter_entries_by_dir(from_dir=ie):
510
                #print "deleting child %s" % child_path
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
511
                self._add_entry((child_path, None, entry.file_id, None))
0.81.8 by Ian Clatworthy
refactor rename_item
512
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
513
    def record_rename(self, old_path, new_path, file_id, old_ie):
514
        new_ie = old_ie.copy()
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
515
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
516
            self.basis_inventory)
517
        new_ie.name = new_basename
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
518
        new_ie.parent_id = new_parent_id
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
519
        new_ie.revision = self.revision_id
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
520
        self._add_entry((old_path, new_path, file_id, new_ie))
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
521
522
    def modify_handler(self, filecmd):
523
        if filecmd.dataref is not None:
524
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
525
        else:
526
            data = filecmd.data
527
        self.debug("modifying %s", filecmd.path)
528
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
529
            filecmd.is_executable, data, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
530
531
    def delete_handler(self, filecmd):
532
        self.debug("deleting %s", filecmd.path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
533
        self._delete_item(filecmd.path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
534
535
    def copy_handler(self, filecmd):
536
        src_path = filecmd.src_path
537
        dest_path = filecmd.dest_path
538
        self.debug("copying %s to %s", src_path, dest_path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
539
        self._copy_item(src_path, dest_path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
540
541
    def rename_handler(self, filecmd):
542
        old_path = filecmd.old_path
543
        new_path = filecmd.new_path
544
        self.debug("renaming %s to %s", old_path, new_path)
545
        self._rename_item(old_path, new_path, self.basis_inventory)
546
547
    def deleteall_handler(self, filecmd):
548
        self.debug("deleting all files (and also all directories)")
549
        # I'm not 100% sure this will work in the delta case.
550
        # But clearing out the basis inventory so that everything
551
        # is added sounds ok in theory ...
552
        # We grab a copy as the basis is likely to be cached and
553
        # we don't want to destroy the cached version
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
554
        self.basis_inventory = copy_inventory(self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
555
        self._delete_all_items(self.basis_inventory)