/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,
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
26
    serializer,
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
27
    )
28
from bzrlib.plugins.fastimport import helpers, processor
29
30
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
31
_serializer_handles_escaping = hasattr(serializer.Serializer,
32
    'squashes_xml_invalid_characters')
33
34
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
35
def copy_inventory(inv):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
36
    # This currently breaks revision-id matching
37
    #if hasattr(inv, "_get_mutable_inventory"):
38
    #    # TODO: Make this a public API on inventory
39
    #    return inv._get_mutable_inventory()
40
41
    # TODO: Shallow copy - deep inventory copying is expensive
42
    return inv.copy()
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
43
44
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
45
class GenericCommitHandler(processor.CommitHandler):
46
    """Base class for Bazaar CommitHandlers."""
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
47
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
48
    def __init__(self, command, cache_mgr, rev_store, verbose=False,
49
        prune_empty_dirs=True):
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
50
        super(GenericCommitHandler, self).__init__(command)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
51
        self.cache_mgr = cache_mgr
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
52
        self.rev_store = rev_store
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
53
        self.verbose = verbose
0.64.159 by Ian Clatworthy
make the file-id cache optional and branch-ref aware
54
        self.branch_ref = command.ref
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
55
        self.prune_empty_dirs = prune_empty_dirs
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
56
        # This tracks path->file-id for things we're creating this commit.
57
        # If the same path is created multiple times, we need to warn the
58
        # user and add it just once.
59
        self._new_file_ids = {}
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
60
61
    def pre_process_files(self):
62
        """Prepare for committing."""
63
        self.revision_id = self.gen_revision_id()
64
        # cache of texts for this commit, indexed by file-id
65
        self.lines_for_commit = {}
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
66
        #if self.rev_store.expects_rich_root():
67
        self.lines_for_commit[inventory.ROOT_ID] = []
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
68
69
        # Track the heads and get the real parent list
70
        parents = self.cache_mgr.track_heads(self.command)
71
72
        # Convert the parent commit-ids to bzr revision-ids
73
        if parents:
74
            self.parents = [self.cache_mgr.revision_ids[p]
75
                for p in parents]
76
        else:
77
            self.parents = []
78
        self.debug("%s id: %s, parents: %s", self.command.id,
79
            self.revision_id, str(self.parents))
80
0.85.2 by Ian Clatworthy
improve per-file graph generation
81
        # Tell the RevisionStore we're starting a new commit
82
        self.revision = self.build_revision()
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
83
        self.parent_invs = [self.get_inventory(p) for p in self.parents]
0.85.2 by Ian Clatworthy
improve per-file graph generation
84
        self.rev_store.start_new_revision(self.revision, self.parents,
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
85
            self.parent_invs)
0.85.2 by Ian Clatworthy
improve per-file graph generation
86
87
        # cache of per-file parents for this commit, indexed by file-id
88
        self.per_file_parents_for_commit = {}
89
        if self.rev_store.expects_rich_root():
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
90
            self.per_file_parents_for_commit[inventory.ROOT_ID] = ()
0.85.2 by Ian Clatworthy
improve per-file graph generation
91
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
92
        # Keep the basis inventory. This needs to be treated as read-only.
93
        if len(self.parents) == 0:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
94
            self.basis_inventory = self._init_inventory()
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
95
        else:
96
            self.basis_inventory = self.get_inventory(self.parents[0])
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
97
        if hasattr(self.basis_inventory, "root_id"):
98
            self.inventory_root_id = self.basis_inventory.root_id
99
        else:
100
            self.inventory_root_id = self.basis_inventory.root.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
101
102
        # directory-path -> inventory-entry for current inventory
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
103
        self.directory_entries = {}
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
104
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
105
    def _init_inventory(self):
106
        return self.rev_store.init_inventory(self.revision_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
107
108
    def get_inventory(self, revision_id):
109
        """Get the inventory for a revision id."""
110
        try:
111
            inv = self.cache_mgr.inventories[revision_id]
112
        except KeyError:
113
            if self.verbose:
0.64.148 by Ian Clatworthy
handle delete of unknown file in chk formats & reduce noise
114
                self.mutter("get_inventory cache miss for %s", revision_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
115
            # Not cached so reconstruct from the RevisionStore
116
            inv = self.rev_store.get_inventory(revision_id)
117
            self.cache_mgr.inventories[revision_id] = inv
118
        return inv
119
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
120
    def _get_lines(self, file_id):
121
        """Get the lines for a file-id."""
122
        return self.lines_for_commit[file_id]
123
0.85.2 by Ian Clatworthy
improve per-file graph generation
124
    def _get_per_file_parents(self, file_id):
125
        """Get the lines for a file-id."""
126
        return self.per_file_parents_for_commit[file_id]
127
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
128
    def _get_inventories(self, revision_ids):
129
        """Get the inventories for revision-ids.
130
        
131
        This is a callback used by the RepositoryStore to
132
        speed up inventory reconstruction.
133
        """
134
        present = []
135
        inventories = []
136
        # If an inventory is in the cache, we assume it was
137
        # successfully loaded into the revision store
138
        for revision_id in revision_ids:
139
            try:
140
                inv = self.cache_mgr.inventories[revision_id]
141
                present.append(revision_id)
142
            except KeyError:
143
                if self.verbose:
144
                    self.note("get_inventories cache miss for %s", revision_id)
145
                # Not cached so reconstruct from the revision store
146
                try:
147
                    inv = self.get_inventory(revision_id)
148
                    present.append(revision_id)
149
                except:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
150
                    inv = self._init_inventory()
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
151
                self.cache_mgr.inventories[revision_id] = inv
152
            inventories.append(inv)
153
        return present, inventories
154
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
155
    def bzr_file_id_and_new(self, path):
156
        """Get a Bazaar file identifier and new flag for a path.
157
        
158
        :return: file_id, is_new where
159
          is_new = True if the file_id is newly created
160
        """
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
161
        # Try the basis inventory
162
        id = self.basis_inventory.path2id(path)
163
        if id is not None:
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
164
            return id, False
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
165
        
166
        # Try the other inventories
167
        if len(self.parents) > 1:
168
            for inv in self.parent_invs[1:]:
169
                id = self.basis_inventory.path2id(path)
170
                if id is not None:
171
                    return id, False
172
173
        # Doesn't exist yet so create it
174
        id = generate_ids.gen_file_id(path)
175
        self.debug("Generated new file id %s for '%s' in revision-id '%s'",
176
            id, path, self.revision_id)
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
177
        self._new_file_ids[path] = id
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
178
        return id, True
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
179
180
    def bzr_file_id(self, path):
181
        """Get a Bazaar file identifier for a path."""
182
        return self.bzr_file_id_and_new(path)[0]
183
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
184
    def _format_name_email(self, name, email):
185
        """Format name & email as a string."""
186
        if email:
187
            return "%s <%s>" % (name, email)
188
        else:
189
            return name
190
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
191
    def gen_revision_id(self):
192
        """Generate a revision id.
193
194
        Subclasses may override this to produce deterministic ids say.
195
        """
196
        committer = self.command.committer
197
        # Perhaps 'who' being the person running the import is ok? If so,
198
        # it might be a bit quicker and give slightly better compression?
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
199
        who = self._format_name_email(committer[0], committer[1])
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
200
        timestamp = committer[2]
201
        return generate_ids.gen_revision_id(who, timestamp)
202
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
203
    def build_revision(self):
204
        rev_props = {}
205
        committer = self.command.committer
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
206
        who = self._format_name_email(committer[0], committer[1])
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
207
        author = self.command.author
208
        if author is not None:
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
209
            author_id = self._format_name_email(author[0], author[1])
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
210
            if author_id != who:
211
                rev_props['author'] = author_id
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
212
        message = self.command.message
213
        if not _serializer_handles_escaping:
214
            # We need to assume the bad ol' days
215
            message = helpers.escape_commit_message(message)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
216
        return revision.Revision(
217
           timestamp=committer[2],
218
           timezone=committer[3],
219
           committer=who,
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
220
           message=message,
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
221
           revision_id=self.revision_id,
222
           properties=rev_props,
223
           parent_ids=self.parents)
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
224
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
225
    def _modify_item(self, path, kind, is_executable, data, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
226
        """Add to or change an item in the inventory."""
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
227
        # If we've already added this, warn the user that we're ignoring it.
228
        # In the future, it might be nice to double check that the new data
229
        # is the same as the old but, frankly, exporters should be fixed
230
        # not to produce bad data streams in the first place ...
231
        existing = self._new_file_ids.get(path)
232
        if existing:
233
            self.warning("%s already added in this commit - ignoring" % (path,))
234
            return
235
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
236
        # Create the new InventoryEntry
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
237
        basename, parent_id = self._ensure_directory(path, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
238
        file_id = self.bzr_file_id(path)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
239
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
240
        ie.revision = self.revision_id
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
241
        if kind == 'file':
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
242
            ie.executable = is_executable
243
            lines = osutils.split_lines(data)
244
            ie.text_sha1 = osutils.sha_strings(lines)
245
            ie.text_size = sum(map(len, lines))
246
            self.lines_for_commit[file_id] = lines
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
247
        elif kind == 'symlink':
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
248
            ie.symlink_target = data.encode('utf8')
249
            # There are no lines stored for a symlink so
250
            # make sure the cache used by get_lines knows that
251
            self.lines_for_commit[file_id] = []
252
        else:
253
            raise errors.BzrError("Cannot import items of kind '%s' yet" %
254
                (kind,))
255
        # Record it
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
256
        if file_id in inv:
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
257
            old_ie = inv[file_id]
258
            if old_ie.kind == 'directory':
259
                self.record_delete(path, old_ie)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
260
            self.record_changed(path, ie, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
261
        else:
0.64.165 by Ian Clatworthy
handle adding a file to a dir deleted in the same commit
262
            try:
263
                self.record_new(path, ie)
264
            except:
0.64.167 by Ian Clatworthy
incremental packing for chk formats
265
                print "failed to add path '%s' with entry '%s' in command %s" \
266
                    % (path, ie, self.command.id)
267
                print "parent's children are:\n%r\n" % (ie.parent_id.children,)
0.64.165 by Ian Clatworthy
handle adding a file to a dir deleted in the same commit
268
                raise
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
269
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
270
    def _ensure_directory(self, path, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
271
        """Ensure that the containing directory exists for 'path'"""
272
        dirname, basename = osutils.split(path)
273
        if dirname == '':
274
            # the root node doesn't get updated
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
275
            return basename, self.inventory_root_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
276
        try:
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
277
            ie = self._get_directory_entry(inv, dirname)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
278
        except KeyError:
279
            # We will create this entry, since it doesn't exist
280
            pass
281
        else:
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
282
            return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
283
284
        # No directory existed, we will just create one, first, make sure
285
        # the parent exists
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
286
        dir_basename, parent_id = self._ensure_directory(dirname, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
287
        dir_file_id = self.bzr_file_id(dirname)
288
        ie = inventory.entry_factory['directory'](dir_file_id,
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
289
            dir_basename, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
290
        ie.revision = self.revision_id
291
        self.directory_entries[dirname] = ie
292
        # There are no lines stored for a directory so
293
        # make sure the cache used by get_lines knows that
294
        self.lines_for_commit[dir_file_id] = []
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
295
296
        # It's possible that a file or symlink with that file-id
297
        # already exists. If it does, we need to delete it.
298
        if dir_file_id in inv:
299
            self.record_delete(dirname, ie)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
300
        self.record_new(dirname, ie)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
301
        return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
302
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
303
    def _get_directory_entry(self, inv, dirname):
304
        """Get the inventory entry for a directory.
305
        
306
        Raises KeyError if dirname is not a directory in inv.
307
        """
308
        result = self.directory_entries.get(dirname)
309
        if result is None:
0.64.146 by Ian Clatworthy
fix first file is in a subdirectory bug for chk formats
310
            try:
311
                file_id = inv.path2id(dirname)
312
            except errors.NoSuchId:
313
                # In a CHKInventory, this is raised if there's no root yet
314
                raise KeyError
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
315
            if file_id is None:
316
                raise KeyError
317
            result = inv[file_id]
318
            # dirname must be a directory for us to return it
319
            if result.kind == 'directory':
320
                self.directory_entries[dirname] = result
321
            else:
322
                raise KeyError
323
        return result
324
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
325
    def _delete_item(self, path, inv):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
326
        newly_added = self._new_file_ids.get(path)
327
        if newly_added:
328
            # We've only just added this path earlier in this commit.
329
            file_id = newly_added
330
            # note: delta entries look like (old, new, file-id, ie)
331
            ie = self._delta_entries_by_fileid[file_id][3]
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
332
        else:
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
333
            file_id = inv.path2id(path)
334
            if file_id is None:
335
                self.mutter("ignoring delete of %s as not in inventory", path)
336
                return
337
            try:
338
                ie = inv[file_id]
339
            except errors.NoSuchId:
340
                self.mutter("ignoring delete of %s as not in inventory", path)
341
                return
342
        self.record_delete(path, ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
343
344
    def _copy_item(self, src_path, dest_path, inv):
345
        if not self.parents:
346
            self.warning("ignoring copy of %s to %s - no parent revisions",
347
                src_path, dest_path)
348
            return
349
        file_id = inv.path2id(src_path)
350
        if file_id is None:
351
            self.warning("ignoring copy of %s to %s - source does not exist",
352
                src_path, dest_path)
353
            return
354
        ie = inv[file_id]
355
        kind = ie.kind
356
        if kind == 'file':
357
            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
358
            self._modify_item(dest_path, kind, ie.executable, content, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
359
        elif kind == 'symlink':
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
360
            self._modify_item(dest_path, kind, False, ie.symlink_target, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
361
        else:
362
            self.warning("ignoring copy of %s %s - feature not yet supported",
363
                kind, path)
364
365
    def _rename_item(self, old_path, new_path, inv):
0.99.6 by Ian Clatworthy
Handle rename of a just added file
366
        existing = self._new_file_ids.get(old_path)
367
        if existing:
368
            # We've only just added this path earlier in this commit.
369
            # Change the add of old_path to an add of new_path
370
            self._rename_pending_add(old_path, new_path, existing)
371
            return
372
0.81.8 by Ian Clatworthy
refactor rename_item
373
        file_id = inv.path2id(old_path)
0.64.167 by Ian Clatworthy
incremental packing for chk formats
374
        if file_id is None:
375
            self.warning(
376
                "ignoring rename of %s to %s - old path does not exist" %
377
                (old_path, new_path))
378
            return
0.81.8 by Ian Clatworthy
refactor rename_item
379
        ie = inv[file_id]
380
        rev_id = ie.revision
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
381
        new_file_id = inv.path2id(new_path)
382
        if new_file_id is not None:
0.81.9 by Ian Clatworthy
refactor delete_item
383
            self.record_delete(new_path, inv[new_file_id])
0.81.8 by Ian Clatworthy
refactor rename_item
384
        self.record_rename(old_path, new_path, file_id, ie)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
385
0.81.8 by Ian Clatworthy
refactor rename_item
386
        # 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
387
        # that means the loader then needs to know what the "new" text is.
388
        # We therefore must go back to the revision store to get it.
0.81.8 by Ian Clatworthy
refactor rename_item
389
        lines = self.rev_store.get_file_lines(rev_id, file_id)
390
        self.lines_for_commit[file_id] = lines
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
391
392
    def _delete_all_items(self, inv):
393
        for name, root_item in inv.root.children.iteritems():
394
            inv.remove_recursive_id(root_item.file_id)
395
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
396
    def _warn_unless_in_merges(self, fileid, path):
397
        if len(self.parents) <= 1:
398
            return
399
        for parent in self.parents[1:]:
400
            if fileid in self.get_inventory(parent):
401
                return
402
        self.warning("ignoring delete of %s as not in parent inventories", path)
403
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
404
405
class InventoryCommitHandler(GenericCommitHandler):
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
406
    """A CommitHandler that builds and saves Inventory objects."""
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
407
408
    def pre_process_files(self):
409
        super(InventoryCommitHandler, self).pre_process_files()
410
0.64.159 by Ian Clatworthy
make the file-id cache optional and branch-ref aware
411
        # Seed the inventory from the previous one. Note that
412
        # the parent class version of pre_process_files() has
413
        # already set the right basis_inventory for this branch
414
        # but we need to copy it in order to mutate it safely
415
        # without corrupting the cached inventory value.
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
416
        if len(self.parents) == 0:
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
417
            self.inventory = self.basis_inventory
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
418
        else:
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
419
            self.inventory = copy_inventory(self.basis_inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
420
        self.inventory_root = self.inventory.root
421
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
422
        # directory-path -> inventory-entry for current inventory
423
        self.directory_entries = dict(self.inventory.directories())
424
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
425
        # Initialise the inventory revision info as required
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
426
        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
427
            self.inventory.revision_id = self.revision_id
428
        else:
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
429
            # In this revision store, root entries have no knit or weave.
430
            # When serializing out to disk and back in, root.revision is
431
            # always the new revision_id.
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
432
            self.inventory.root.revision = self.revision_id
433
434
    def post_process_files(self):
435
        """Save the revision."""
436
        self.cache_mgr.inventories[self.revision_id] = self.inventory
0.85.2 by Ian Clatworthy
improve per-file graph generation
437
        self.rev_store.load(self.revision, self.inventory, None,
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
438
            lambda file_id: self._get_lines(file_id),
0.85.2 by Ian Clatworthy
improve per-file graph generation
439
            lambda file_id: self._get_per_file_parents(file_id),
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
440
            lambda revision_ids: self._get_inventories(revision_ids))
441
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
442
    def record_new(self, path, ie):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
443
        try:
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
444
            # If this is a merge, the file was most likely added already.
445
            # The per-file parent(s) must therefore be calculated and
446
            # we can't assume there are none.
447
            per_file_parents, ie.revision = \
448
                self.rev_store.get_parents_and_revision_for_entry(ie)
449
            self.per_file_parents_for_commit[ie.file_id] = per_file_parents
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
450
            self.inventory.add(ie)
451
        except errors.DuplicateFileId:
452
            # Directory already exists as a file or symlink
453
            del self.inventory[ie.file_id]
454
            # Try again
455
            self.inventory.add(ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
456
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
457
    def record_changed(self, path, ie, parent_id):
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
458
        # HACK: no API for this (del+add does more than it needs to)
0.85.2 by Ian Clatworthy
improve per-file graph generation
459
        per_file_parents, ie.revision = \
460
            self.rev_store.get_parents_and_revision_for_entry(ie)
461
        self.per_file_parents_for_commit[ie.file_id] = per_file_parents
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
462
        self.inventory._byid[ie.file_id] = ie
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
463
        parent_ie = self.inventory._byid[parent_id]
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
464
        parent_ie.children[ie.name] = ie
465
0.81.9 by Ian Clatworthy
refactor delete_item
466
    def record_delete(self, path, ie):
467
        self.inventory.remove_recursive_id(ie.file_id)
0.81.8 by Ian Clatworthy
refactor rename_item
468
469
    def record_rename(self, old_path, new_path, file_id, ie):
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
470
        # For a rename, the revision-id is always the new one so
471
        # no need to change/set it here
472
        ie.revision = self.revision_id
473
        per_file_parents, _ = \
474
            self.rev_store.get_parents_and_revision_for_entry(ie)
475
        self.per_file_parents_for_commit[file_id] = per_file_parents
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
476
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
477
            self.inventory)
0.81.8 by Ian Clatworthy
refactor rename_item
478
        self.inventory.rename(file_id, new_parent_id, new_basename)
479
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
480
    def modify_handler(self, filecmd):
481
        if filecmd.dataref is not None:
482
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
483
        else:
484
            data = filecmd.data
485
        self.debug("modifying %s", filecmd.path)
486
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
487
            filecmd.is_executable, data, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
488
489
    def delete_handler(self, filecmd):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
490
        self.debug("deleting %s", filecmd.path)
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
491
        self._delete_item(filecmd.path, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
492
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
493
    def copy_handler(self, filecmd):
494
        src_path = filecmd.src_path
495
        dest_path = filecmd.dest_path
496
        self.debug("copying %s to %s", src_path, dest_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
497
        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
498
499
    def rename_handler(self, filecmd):
500
        old_path = filecmd.old_path
501
        new_path = filecmd.new_path
502
        self.debug("renaming %s to %s", old_path, new_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
503
        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
504
505
    def deleteall_handler(self, filecmd):
506
        self.debug("deleting all files (and also all directories)")
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
507
        self._delete_all_items(self.inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
508
509
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
510
class InventoryDeltaCommitHandler(GenericCommitHandler):
511
    """A CommitHandler that builds Inventories by applying a delta."""
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
512
513
    def pre_process_files(self):
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
514
        super(InventoryDeltaCommitHandler, self).pre_process_files()
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
515
        self._dirs_that_might_become_empty = set()
516
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
517
        # A given file-id can only appear once so we accumulate
518
        # the entries in a dict then build the actual delta at the end
519
        self._delta_entries_by_fileid = {}
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
520
        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
521
            if self.parents:
522
                old_path = ''
523
            else:
524
                old_path = None
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
525
            # 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
526
            # and for non rich-root inventories
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
527
            root_id = inventory.ROOT_ID
528
            root_ie = inventory.InventoryDirectory(root_id, u'', None)
529
            root_ie.revision = self.revision_id
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
530
            self._add_entry((old_path, '', root_id, root_ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
531
532
    def post_process_files(self):
533
        """Save the revision."""
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
534
        delta = self._get_final_delta()
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
535
        inv = self.rev_store.load_using_delta(self.revision,
536
            self.basis_inventory, delta, None,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
537
            lambda file_id: self._get_lines(file_id),
0.85.2 by Ian Clatworthy
improve per-file graph generation
538
            lambda file_id: self._get_per_file_parents(file_id),
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
539
            lambda revision_ids: self._get_inventories(revision_ids))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
540
        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
541
        #print "committed %s" % self.revision_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
542
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
543
    def _get_final_delta(self):
544
        """Generate the final delta.
545
546
        Smart post-processing of changes, e.g. pruning of directories
547
        that would become empty, goes here.
548
        """
549
        delta = list(self._delta_entries_by_fileid.values())
550
        if self.prune_empty_dirs and self._dirs_that_might_become_empty:
551
            candidates = osutils.minimum_path_selection(
552
                self._dirs_that_might_become_empty)
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
553
            never_born = set()
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
554
            for path, file_id in self._empty_after_delta(delta, candidates):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
555
                newly_added = self._new_file_ids.get(path)
556
                if newly_added:
557
                    never_born.add(newly_added)
558
                else:
559
                    delta.append((path, None, file_id, None))
560
            # Clean up entries that got deleted before they were ever added
561
            if never_born:
562
                delta = [de for de in delta if de[2] not in never_born]
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
563
        return delta
564
565
    def _empty_after_delta(self, delta, candidates):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
566
        #self.mutter("delta so far is:\n%s" % "\n".join([str(de) for de in delta]))
567
        #self.mutter("candidates for deletion are:\n%s" % "\n".join([c for c in candidates]))
568
        new_inv = self._get_proposed_inventory(delta)
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
569
        result = []
570
        for dir in candidates:
571
            file_id = new_inv.path2id(dir)
0.64.219 by Ian Clatworthy
More robust implicit delete logic when file-id not found
572
            if file_id is None:
573
                continue
0.96.2 by Ian Clatworthy
test and fix for implicit directory delete recursing up
574
            ie = new_inv[file_id]
575
            if len(ie.children) == 0:
576
                result.append((dir, file_id))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
577
                if self.verbose:
578
                    self.note("pruning empty directory %s" % (dir,))
0.96.2 by Ian Clatworthy
test and fix for implicit directory delete recursing up
579
                # Check parents in case deleting this dir makes *them* empty
580
                while True:
581
                    file_id = ie.parent_id
582
                    if file_id == inventory.ROOT_ID:
583
                        # We've reach the root
584
                        break
585
                    try:
586
                        ie = new_inv[file_id]
587
                    except errors.NoSuchId:
588
                        break
589
                    if len(ie.children) > 1:
590
                        break
591
                    dir = new_inv.id2path(file_id)
592
                    result.append((dir, file_id))
593
                    if self.verbose:
594
                        self.note("pruning empty directory parent %s" % (dir,))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
595
        return result
596
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
597
    def _get_proposed_inventory(self, delta):
598
        if len(self.parents):
599
            new_inv = self.basis_inventory._get_mutable_inventory()
600
            new_inv.apply_delta(delta)
601
        else:
602
            new_inv = inventory.Inventory(revision_id=self.revision_id)
603
            # This is set in the delta so remove it to prevent a duplicate
604
            del new_inv[inventory.ROOT_ID]
605
            new_inv.apply_delta(delta)
606
        return new_inv
607
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
608
    def _add_entry(self, entry):
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
609
        # We need to combine the data if multiple entries have the same file-id.
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
610
        # For example, a rename followed by a modification looks like:
611
        #
612
        # (x, y, f, e) & (y, y, f, g) => (x, y, f, g)
613
        #
614
        # Likewise, a modification followed by a rename looks like:
615
        #
616
        # (x, x, f, e) & (x, y, f, g) => (x, y, f, g)
617
        #
618
        # Here's a rename followed by a delete and a modification followed by
619
        # a delete:
620
        #
621
        # (x, y, f, e) & (y, None, f, None) => (x, None, f, None)
622
        # (x, x, f, e) & (x, None, f, None) => (x, None, f, None)
623
        #
624
        # In summary, we use the original old-path, new new-path and new ie
625
        # when combining entries.
0.85.2 by Ian Clatworthy
improve per-file graph generation
626
        old_path = entry[0]
627
        new_path = entry[1]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
628
        file_id = entry[2]
0.85.2 by Ian Clatworthy
improve per-file graph generation
629
        ie = entry[3]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
630
        existing = self._delta_entries_by_fileid.get(file_id, None)
631
        if existing is not None:
0.85.2 by Ian Clatworthy
improve per-file graph generation
632
            old_path = existing[0]
633
            entry = (old_path, new_path, file_id, ie)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
634
        if new_path is None and old_path is None:
635
            # This is a delete cancelling a previous add
636
            del self._delta_entries_by_fileid[file_id]
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
637
            parent_dir = osutils.dirname(existing[1])
638
            self.mutter("cancelling add of %s with parent %s" % (existing[1], parent_dir))
639
            if parent_dir:
640
                self._dirs_that_might_become_empty.add(parent_dir)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
641
            return
642
        else:
643
            self._delta_entries_by_fileid[file_id] = entry
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
644
0.99.6 by Ian Clatworthy
Handle rename of a just added file
645
        # Collect parent directories that might become empty
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
646
        if new_path is None:
647
            # delete
648
            parent_dir = osutils.dirname(old_path)
649
            # note: no need to check the root
650
            if parent_dir:
651
                self._dirs_that_might_become_empty.add(parent_dir)
652
        elif old_path is not None and old_path != new_path:
653
            # rename
654
            old_parent_dir = osutils.dirname(old_path)
655
            new_parent_dir = osutils.dirname(new_path)
656
            if old_parent_dir and old_parent_dir != new_parent_dir:
657
                self._dirs_that_might_become_empty.add(old_parent_dir)
658
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
659
        # Calculate the per-file parents, if not already done
660
        if file_id in self.per_file_parents_for_commit:
661
            return
0.85.2 by Ian Clatworthy
improve per-file graph generation
662
        if old_path is None:
663
            # add
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
664
            # If this is a merge, the file was most likely added already.
665
            # The per-file parent(s) must therefore be calculated and
666
            # we can't assume there are none.
667
            per_file_parents, ie.revision = \
668
                self.rev_store.get_parents_and_revision_for_entry(ie)
669
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
670
        elif new_path is None:
671
            # delete
672
            pass
673
        elif old_path != new_path:
674
            # rename
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
675
            per_file_parents, _ = \
676
                self.rev_store.get_parents_and_revision_for_entry(ie)
677
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
678
        else:
679
            # modify
680
            per_file_parents, ie.revision = \
681
                self.rev_store.get_parents_and_revision_for_entry(ie)
682
            self.per_file_parents_for_commit[file_id] = per_file_parents
683
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
684
    def record_new(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
685
        self._add_entry((None, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
686
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
687
    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
688
        self._add_entry((path, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
689
0.81.9 by Ian Clatworthy
refactor delete_item
690
    def record_delete(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
691
        self._add_entry((path, None, ie.file_id, None))
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
692
        if ie.kind == 'directory':
0.64.187 by Ian Clatworthy
fix inv-delta generation when deleting directories
693
            for child_relpath, entry in \
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
694
                self.basis_inventory.iter_entries_by_dir(from_dir=ie):
0.64.187 by Ian Clatworthy
fix inv-delta generation when deleting directories
695
                child_path = osutils.pathjoin(path, child_relpath)
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
696
                self._add_entry((child_path, None, entry.file_id, None))
0.81.8 by Ian Clatworthy
refactor rename_item
697
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
698
    def record_rename(self, old_path, new_path, file_id, old_ie):
699
        new_ie = old_ie.copy()
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
700
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
701
            self.basis_inventory)
702
        new_ie.name = new_basename
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
703
        new_ie.parent_id = new_parent_id
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
704
        new_ie.revision = self.revision_id
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
705
        self._add_entry((old_path, new_path, file_id, new_ie))
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
706
0.99.6 by Ian Clatworthy
Handle rename of a just added file
707
    def _rename_pending_add(self, old_path, new_path, file_id):
708
        """Instead of adding old-path, add new-path instead."""
709
        # note: delta entries look like (old, new, file-id, ie)
710
        old_ie = self._delta_entries_by_fileid[file_id][3]
711
712
        # Delete the old path. Note that this might trigger implicit
713
        # deletion of newly created parents that could now become empty.
714
        self.record_delete(old_path, old_ie)
715
716
        # Update the dictionary used for tracking new file-ids
717
        del self._new_file_ids[old_path]
718
        self._new_file_ids[new_path] = file_id
719
720
        # Create the new InventoryEntry
721
        kind = old_ie.kind
722
        basename, parent_id = self._ensure_directory(new_path,
723
            self.basis_inventory)
724
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
725
        ie.revision = self.revision_id
726
        if kind == 'file':
727
            ie.executable = old_ie.executable
728
            ie.text_sha1 = old_ie.text_sha1
729
            ie.text_size = old_ie.text_size
730
        elif kind == 'symlink':
731
            ie.symlink_target = old_ie.symlink_target
732
733
        # Record it
734
        self.record_new(new_path, ie)
735
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
736
    def modify_handler(self, filecmd):
737
        if filecmd.dataref is not None:
738
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
739
        else:
740
            data = filecmd.data
741
        self.debug("modifying %s", filecmd.path)
742
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
743
            filecmd.is_executable, data, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
744
745
    def delete_handler(self, filecmd):
746
        self.debug("deleting %s", filecmd.path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
747
        self._delete_item(filecmd.path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
748
749
    def copy_handler(self, filecmd):
750
        src_path = filecmd.src_path
751
        dest_path = filecmd.dest_path
752
        self.debug("copying %s to %s", src_path, dest_path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
753
        self._copy_item(src_path, dest_path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
754
755
    def rename_handler(self, filecmd):
756
        old_path = filecmd.old_path
757
        new_path = filecmd.new_path
758
        self.debug("renaming %s to %s", old_path, new_path)
759
        self._rename_item(old_path, new_path, self.basis_inventory)
760
761
    def deleteall_handler(self, filecmd):
762
        self.debug("deleting all files (and also all directories)")
763
        # I'm not 100% sure this will work in the delta case.
764
        # But clearing out the basis inventory so that everything
765
        # is added sounds ok in theory ...
766
        # We grab a copy as the basis is likely to be cached and
767
        # we don't want to destroy the cached version
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
768
        self.basis_inventory = copy_inventory(self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
769
        self._delete_all_items(self.basis_inventory)