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