/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):
0.99.8 by Ian Clatworthy
handle copy of a newly added file
345
        newly_added = self._new_file_ids.get(src_path)
346
        if newly_added:
347
            # We've only just added this path earlier in this commit.
348
            file_id = newly_added
349
            # note: delta entries look like (old, new, file-id, ie)
350
            ie = self._delta_entries_by_fileid[file_id][3]
351
        else:
352
            file_id = inv.path2id(src_path)
353
            if file_id is None:
354
                self.warning("ignoring copy of %s to %s - source does not exist",
355
                    src_path, dest_path)
356
                return
357
            ie = inv[file_id]
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
358
        kind = ie.kind
359
        if kind == 'file':
0.99.8 by Ian Clatworthy
handle copy of a newly added file
360
            if newly_added:
361
                content = ''.join(self.lines_for_commit[file_id])
362
            else:
363
                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
364
            self._modify_item(dest_path, kind, ie.executable, content, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
365
        elif kind == 'symlink':
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
366
            self._modify_item(dest_path, kind, False, ie.symlink_target, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
367
        else:
368
            self.warning("ignoring copy of %s %s - feature not yet supported",
369
                kind, path)
370
371
    def _rename_item(self, old_path, new_path, inv):
0.99.6 by Ian Clatworthy
Handle rename of a just added file
372
        existing = self._new_file_ids.get(old_path)
373
        if existing:
374
            # We've only just added this path earlier in this commit.
375
            # Change the add of old_path to an add of new_path
376
            self._rename_pending_add(old_path, new_path, existing)
377
            return
378
0.81.8 by Ian Clatworthy
refactor rename_item
379
        file_id = inv.path2id(old_path)
0.64.167 by Ian Clatworthy
incremental packing for chk formats
380
        if file_id is None:
381
            self.warning(
382
                "ignoring rename of %s to %s - old path does not exist" %
383
                (old_path, new_path))
384
            return
0.81.8 by Ian Clatworthy
refactor rename_item
385
        ie = inv[file_id]
386
        rev_id = ie.revision
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
387
        new_file_id = inv.path2id(new_path)
388
        if new_file_id is not None:
0.81.9 by Ian Clatworthy
refactor delete_item
389
            self.record_delete(new_path, inv[new_file_id])
0.81.8 by Ian Clatworthy
refactor rename_item
390
        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
391
0.81.8 by Ian Clatworthy
refactor rename_item
392
        # 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
393
        # that means the loader then needs to know what the "new" text is.
394
        # We therefore must go back to the revision store to get it.
0.81.8 by Ian Clatworthy
refactor rename_item
395
        lines = self.rev_store.get_file_lines(rev_id, file_id)
396
        self.lines_for_commit[file_id] = lines
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
397
398
    def _delete_all_items(self, inv):
399
        for name, root_item in inv.root.children.iteritems():
400
            inv.remove_recursive_id(root_item.file_id)
401
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
402
    def _warn_unless_in_merges(self, fileid, path):
403
        if len(self.parents) <= 1:
404
            return
405
        for parent in self.parents[1:]:
406
            if fileid in self.get_inventory(parent):
407
                return
408
        self.warning("ignoring delete of %s as not in parent inventories", path)
409
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
410
411
class InventoryCommitHandler(GenericCommitHandler):
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
412
    """A CommitHandler that builds and saves Inventory objects."""
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
413
414
    def pre_process_files(self):
415
        super(InventoryCommitHandler, self).pre_process_files()
416
0.64.159 by Ian Clatworthy
make the file-id cache optional and branch-ref aware
417
        # Seed the inventory from the previous one. Note that
418
        # the parent class version of pre_process_files() has
419
        # already set the right basis_inventory for this branch
420
        # but we need to copy it in order to mutate it safely
421
        # 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
422
        if len(self.parents) == 0:
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
423
            self.inventory = self.basis_inventory
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
424
        else:
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
425
            self.inventory = copy_inventory(self.basis_inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
426
        self.inventory_root = self.inventory.root
427
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
428
        # directory-path -> inventory-entry for current inventory
429
        self.directory_entries = dict(self.inventory.directories())
430
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
431
        # Initialise the inventory revision info as required
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
432
        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
433
            self.inventory.revision_id = self.revision_id
434
        else:
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
435
            # In this revision store, root entries have no knit or weave.
436
            # When serializing out to disk and back in, root.revision is
437
            # always the new revision_id.
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
438
            self.inventory.root.revision = self.revision_id
439
440
    def post_process_files(self):
441
        """Save the revision."""
442
        self.cache_mgr.inventories[self.revision_id] = self.inventory
0.85.2 by Ian Clatworthy
improve per-file graph generation
443
        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
444
            lambda file_id: self._get_lines(file_id),
0.85.2 by Ian Clatworthy
improve per-file graph generation
445
            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
446
            lambda revision_ids: self._get_inventories(revision_ids))
447
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
448
    def record_new(self, path, ie):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
449
        try:
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
450
            # If this is a merge, the file was most likely added already.
451
            # The per-file parent(s) must therefore be calculated and
452
            # we can't assume there are none.
453
            per_file_parents, ie.revision = \
454
                self.rev_store.get_parents_and_revision_for_entry(ie)
455
            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
456
            self.inventory.add(ie)
457
        except errors.DuplicateFileId:
458
            # Directory already exists as a file or symlink
459
            del self.inventory[ie.file_id]
460
            # Try again
461
            self.inventory.add(ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
462
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
463
    def record_changed(self, path, ie, parent_id):
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
464
        # HACK: no API for this (del+add does more than it needs to)
0.85.2 by Ian Clatworthy
improve per-file graph generation
465
        per_file_parents, ie.revision = \
466
            self.rev_store.get_parents_and_revision_for_entry(ie)
467
        self.per_file_parents_for_commit[ie.file_id] = per_file_parents
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
468
        self.inventory._byid[ie.file_id] = ie
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
469
        parent_ie = self.inventory._byid[parent_id]
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
470
        parent_ie.children[ie.name] = ie
471
0.81.9 by Ian Clatworthy
refactor delete_item
472
    def record_delete(self, path, ie):
473
        self.inventory.remove_recursive_id(ie.file_id)
0.81.8 by Ian Clatworthy
refactor rename_item
474
475
    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
476
        # For a rename, the revision-id is always the new one so
477
        # no need to change/set it here
478
        ie.revision = self.revision_id
479
        per_file_parents, _ = \
480
            self.rev_store.get_parents_and_revision_for_entry(ie)
481
        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
482
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
483
            self.inventory)
0.81.8 by Ian Clatworthy
refactor rename_item
484
        self.inventory.rename(file_id, new_parent_id, new_basename)
485
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
486
    def modify_handler(self, filecmd):
487
        if filecmd.dataref is not None:
488
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
489
        else:
490
            data = filecmd.data
491
        self.debug("modifying %s", filecmd.path)
492
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
493
            filecmd.is_executable, data, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
494
495
    def delete_handler(self, filecmd):
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
496
        self.debug("deleting %s", filecmd.path)
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
497
        self._delete_item(filecmd.path, self.inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
498
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
499
    def copy_handler(self, filecmd):
500
        src_path = filecmd.src_path
501
        dest_path = filecmd.dest_path
502
        self.debug("copying %s to %s", src_path, dest_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
503
        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
504
505
    def rename_handler(self, filecmd):
506
        old_path = filecmd.old_path
507
        new_path = filecmd.new_path
508
        self.debug("renaming %s to %s", old_path, new_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
509
        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
510
511
    def deleteall_handler(self, filecmd):
512
        self.debug("deleting all files (and also all directories)")
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
513
        self._delete_all_items(self.inventory)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
514
515
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
516
class InventoryDeltaCommitHandler(GenericCommitHandler):
517
    """A CommitHandler that builds Inventories by applying a delta."""
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
518
519
    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
520
        super(InventoryDeltaCommitHandler, self).pre_process_files()
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
521
        self._dirs_that_might_become_empty = set()
522
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
523
        # A given file-id can only appear once so we accumulate
524
        # the entries in a dict then build the actual delta at the end
525
        self._delta_entries_by_fileid = {}
0.84.7 by Ian Clatworthy
CHKInventory support for non rich-root repos working, for simple imports at least
526
        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
527
            if self.parents:
528
                old_path = ''
529
            else:
530
                old_path = None
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
531
            # 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
532
            # and for non rich-root inventories
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
533
            root_id = inventory.ROOT_ID
534
            root_ie = inventory.InventoryDirectory(root_id, u'', None)
535
            root_ie.revision = self.revision_id
0.84.10 by Ian Clatworthy
fix TREE_ROOT delta entry after 1st revision & tweak _delete_item usage
536
            self._add_entry((old_path, '', root_id, root_ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
537
538
    def post_process_files(self):
539
        """Save the revision."""
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
540
        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
541
        inv = self.rev_store.load_using_delta(self.revision,
542
            self.basis_inventory, delta, None,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
543
            lambda file_id: self._get_lines(file_id),
0.85.2 by Ian Clatworthy
improve per-file graph generation
544
            lambda file_id: self._get_per_file_parents(file_id),
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
545
            lambda revision_ids: self._get_inventories(revision_ids))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
546
        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
547
        #print "committed %s" % self.revision_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
548
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
549
    def _get_final_delta(self):
550
        """Generate the final delta.
551
552
        Smart post-processing of changes, e.g. pruning of directories
553
        that would become empty, goes here.
554
        """
555
        delta = list(self._delta_entries_by_fileid.values())
556
        if self.prune_empty_dirs and self._dirs_that_might_become_empty:
557
            candidates = osutils.minimum_path_selection(
558
                self._dirs_that_might_become_empty)
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
559
            never_born = set()
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
560
            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
561
                newly_added = self._new_file_ids.get(path)
562
                if newly_added:
563
                    never_born.add(newly_added)
564
                else:
565
                    delta.append((path, None, file_id, None))
566
            # Clean up entries that got deleted before they were ever added
567
            if never_born:
568
                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
569
        return delta
570
571
    def _empty_after_delta(self, delta, candidates):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
572
        #self.mutter("delta so far is:\n%s" % "\n".join([str(de) for de in delta]))
573
        #self.mutter("candidates for deletion are:\n%s" % "\n".join([c for c in candidates]))
574
        new_inv = self._get_proposed_inventory(delta)
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
575
        result = []
576
        for dir in candidates:
577
            file_id = new_inv.path2id(dir)
0.64.219 by Ian Clatworthy
More robust implicit delete logic when file-id not found
578
            if file_id is None:
579
                continue
0.96.2 by Ian Clatworthy
test and fix for implicit directory delete recursing up
580
            ie = new_inv[file_id]
581
            if len(ie.children) == 0:
582
                result.append((dir, file_id))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
583
                if self.verbose:
584
                    self.note("pruning empty directory %s" % (dir,))
0.96.2 by Ian Clatworthy
test and fix for implicit directory delete recursing up
585
                # Check parents in case deleting this dir makes *them* empty
586
                while True:
587
                    file_id = ie.parent_id
588
                    if file_id == inventory.ROOT_ID:
589
                        # We've reach the root
590
                        break
591
                    try:
592
                        ie = new_inv[file_id]
593
                    except errors.NoSuchId:
594
                        break
595
                    if len(ie.children) > 1:
596
                        break
597
                    dir = new_inv.id2path(file_id)
598
                    result.append((dir, file_id))
599
                    if self.verbose:
600
                        self.note("pruning empty directory parent %s" % (dir,))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
601
        return result
602
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
603
    def _get_proposed_inventory(self, delta):
604
        if len(self.parents):
605
            new_inv = self.basis_inventory._get_mutable_inventory()
606
        else:
607
            new_inv = inventory.Inventory(revision_id=self.revision_id)
608
            # This is set in the delta so remove it to prevent a duplicate
609
            del new_inv[inventory.ROOT_ID]
0.99.9 by Ian Clatworthy
better diagnostics on inconsistent delta
610
        try:
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
611
            new_inv.apply_delta(delta)
0.99.9 by Ian Clatworthy
better diagnostics on inconsistent delta
612
        except errors.InconsistentDelta:
613
            self.mutter("INCONSISTENT DELTA IS:\n%s" % "\n".join([str(de) for de in delta]))
614
            raise
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
615
        return new_inv
616
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
617
    def _add_entry(self, entry):
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
618
        # 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
619
        # For example, a rename followed by a modification looks like:
620
        #
621
        # (x, y, f, e) & (y, y, f, g) => (x, y, f, g)
622
        #
623
        # Likewise, a modification followed by a rename looks like:
624
        #
625
        # (x, x, f, e) & (x, y, f, g) => (x, y, f, g)
626
        #
627
        # Here's a rename followed by a delete and a modification followed by
628
        # a delete:
629
        #
630
        # (x, y, f, e) & (y, None, f, None) => (x, None, f, None)
631
        # (x, x, f, e) & (x, None, f, None) => (x, None, f, None)
632
        #
633
        # In summary, we use the original old-path, new new-path and new ie
634
        # when combining entries.
0.85.2 by Ian Clatworthy
improve per-file graph generation
635
        old_path = entry[0]
636
        new_path = entry[1]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
637
        file_id = entry[2]
0.85.2 by Ian Clatworthy
improve per-file graph generation
638
        ie = entry[3]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
639
        existing = self._delta_entries_by_fileid.get(file_id, None)
640
        if existing is not None:
0.85.2 by Ian Clatworthy
improve per-file graph generation
641
            old_path = existing[0]
642
            entry = (old_path, new_path, file_id, ie)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
643
        if new_path is None and old_path is None:
644
            # This is a delete cancelling a previous add
645
            del self._delta_entries_by_fileid[file_id]
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
646
            parent_dir = osutils.dirname(existing[1])
647
            self.mutter("cancelling add of %s with parent %s" % (existing[1], parent_dir))
648
            if parent_dir:
649
                self._dirs_that_might_become_empty.add(parent_dir)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
650
            return
651
        else:
652
            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
653
0.99.6 by Ian Clatworthy
Handle rename of a just added file
654
        # Collect parent directories that might become empty
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
655
        if new_path is None:
656
            # delete
657
            parent_dir = osutils.dirname(old_path)
658
            # note: no need to check the root
659
            if parent_dir:
660
                self._dirs_that_might_become_empty.add(parent_dir)
661
        elif old_path is not None and old_path != new_path:
662
            # rename
663
            old_parent_dir = osutils.dirname(old_path)
664
            new_parent_dir = osutils.dirname(new_path)
665
            if old_parent_dir and old_parent_dir != new_parent_dir:
666
                self._dirs_that_might_become_empty.add(old_parent_dir)
667
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
668
        # Calculate the per-file parents, if not already done
669
        if file_id in self.per_file_parents_for_commit:
670
            return
0.85.2 by Ian Clatworthy
improve per-file graph generation
671
        if old_path is None:
672
            # add
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
673
            # If this is a merge, the file was most likely added already.
674
            # The per-file parent(s) must therefore be calculated and
675
            # we can't assume there are none.
676
            per_file_parents, ie.revision = \
677
                self.rev_store.get_parents_and_revision_for_entry(ie)
678
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
679
        elif new_path is None:
680
            # delete
681
            pass
682
        elif old_path != new_path:
683
            # rename
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
684
            per_file_parents, _ = \
685
                self.rev_store.get_parents_and_revision_for_entry(ie)
686
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
687
        else:
688
            # modify
689
            per_file_parents, ie.revision = \
690
                self.rev_store.get_parents_and_revision_for_entry(ie)
691
            self.per_file_parents_for_commit[file_id] = per_file_parents
692
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
693
    def record_new(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
694
        self._add_entry((None, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
695
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
696
    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
697
        self._add_entry((path, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
698
0.81.9 by Ian Clatworthy
refactor delete_item
699
    def record_delete(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
700
        self._add_entry((path, None, ie.file_id, None))
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
701
        if ie.kind == 'directory':
0.64.187 by Ian Clatworthy
fix inv-delta generation when deleting directories
702
            for child_relpath, entry in \
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
703
                self.basis_inventory.iter_entries_by_dir(from_dir=ie):
0.64.187 by Ian Clatworthy
fix inv-delta generation when deleting directories
704
                child_path = osutils.pathjoin(path, child_relpath)
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
705
                self._add_entry((child_path, None, entry.file_id, None))
0.81.8 by Ian Clatworthy
refactor rename_item
706
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
707
    def record_rename(self, old_path, new_path, file_id, old_ie):
708
        new_ie = old_ie.copy()
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
709
        new_basename, new_parent_id = self._ensure_directory(new_path,
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
710
            self.basis_inventory)
711
        new_ie.name = new_basename
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
712
        new_ie.parent_id = new_parent_id
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
713
        new_ie.revision = self.revision_id
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
714
        self._add_entry((old_path, new_path, file_id, new_ie))
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
715
0.99.6 by Ian Clatworthy
Handle rename of a just added file
716
    def _rename_pending_add(self, old_path, new_path, file_id):
717
        """Instead of adding old-path, add new-path instead."""
718
        # note: delta entries look like (old, new, file-id, ie)
719
        old_ie = self._delta_entries_by_fileid[file_id][3]
720
721
        # Delete the old path. Note that this might trigger implicit
722
        # deletion of newly created parents that could now become empty.
723
        self.record_delete(old_path, old_ie)
724
725
        # Update the dictionary used for tracking new file-ids
726
        del self._new_file_ids[old_path]
727
        self._new_file_ids[new_path] = file_id
728
729
        # Create the new InventoryEntry
730
        kind = old_ie.kind
731
        basename, parent_id = self._ensure_directory(new_path,
732
            self.basis_inventory)
733
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
734
        ie.revision = self.revision_id
735
        if kind == 'file':
736
            ie.executable = old_ie.executable
737
            ie.text_sha1 = old_ie.text_sha1
738
            ie.text_size = old_ie.text_size
739
        elif kind == 'symlink':
740
            ie.symlink_target = old_ie.symlink_target
741
742
        # Record it
743
        self.record_new(new_path, ie)
744
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
745
    def modify_handler(self, filecmd):
746
        if filecmd.dataref is not None:
747
            data = self.cache_mgr.fetch_blob(filecmd.dataref)
748
        else:
749
            data = filecmd.data
750
        self.debug("modifying %s", filecmd.path)
751
        self._modify_item(filecmd.path, filecmd.kind,
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
752
            filecmd.is_executable, data, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
753
754
    def delete_handler(self, filecmd):
755
        self.debug("deleting %s", filecmd.path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
756
        self._delete_item(filecmd.path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
757
758
    def copy_handler(self, filecmd):
759
        src_path = filecmd.src_path
760
        dest_path = filecmd.dest_path
761
        self.debug("copying %s to %s", src_path, dest_path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
762
        self._copy_item(src_path, dest_path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
763
764
    def rename_handler(self, filecmd):
765
        old_path = filecmd.old_path
766
        new_path = filecmd.new_path
767
        self.debug("renaming %s to %s", old_path, new_path)
768
        self._rename_item(old_path, new_path, self.basis_inventory)
769
770
    def deleteall_handler(self, filecmd):
771
        self.debug("deleting all files (and also all directories)")
772
        # I'm not 100% sure this will work in the delta case.
773
        # But clearing out the basis inventory so that everything
774
        # is added sounds ok in theory ...
775
        # We grab a copy as the basis is likely to be cached and
776
        # we don't want to destroy the cached version
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
777
        self.basis_inventory = copy_inventory(self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
778
        self._delete_all_items(self.basis_inventory)