/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
0.64.334 by Jelmer Vernooij
Remove old FSF address. Thanks Dan Callaghan.
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
15
16
"""CommitHandlers that build and save revisions & their inventories."""
17
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
18
from __future__ import absolute_import
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
19
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
20
from ... import (
0.123.9 by Jelmer Vernooij
Provide stubs for logging functions no longer provided by python-fastimport.
21
    debug,
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
22
    errors,
23
    generate_ids,
24
    osutils,
25
    revision,
26
    )
6670.4.3 by Jelmer Vernooij
Fix more imports.
27
from ...bzr import (
28
    inventory,
6670.4.10 by Jelmer Vernooij
Move serializer to bzr.
29
    serializer,
6670.4.3 by Jelmer Vernooij
Fix more imports.
30
    )
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
31
from ...trace import (
0.123.9 by Jelmer Vernooij
Provide stubs for logging functions no longer provided by python-fastimport.
32
    mutter,
33
    note,
34
    warning,
35
    )
0.123.2 by Jelmer Vernooij
Split out fastimport, import it from the system.
36
from fastimport import (
0.123.1 by Jelmer Vernooij
Move pure-fastimport code into its own directory, in preparation of splitting it into a separate package.
37
    helpers,
38
    processor,
39
    )
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
40
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
41
from .helpers import (
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
42
    mode_to_kind,
43
    )
44
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
45
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
46
_serializer_handles_escaping = hasattr(serializer.Serializer,
7143.15.2 by Jelmer Vernooij
Run autopep8.
47
                                       'squashes_xml_invalid_characters')
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
48
0.64.318 by Jelmer Vernooij
Avoid Inventory.copy, which has disappeared in newer versions of Bazaar.
49
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
50
def copy_inventory(inv):
0.64.318 by Jelmer Vernooij
Avoid Inventory.copy, which has disappeared in newer versions of Bazaar.
51
    entries = inv.iter_entries_by_dir()
0.64.319 by Jelmer Vernooij
fix typo.
52
    inv = inventory.Inventory(None, inv.revision_id)
0.64.318 by Jelmer Vernooij
Avoid Inventory.copy, which has disappeared in newer versions of Bazaar.
53
    for path, inv_entry in entries:
54
        inv.add(inv_entry.copy())
55
    return inv
0.84.3 by Ian Clatworthy
fix inventory copying when using deltas
56
57
7027.2.4 by Jelmer Vernooij
Drop multiple CommitHandler implementations.
58
class CommitHandler(processor.CommitHandler):
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
59
    """Base class for Bazaar CommitHandlers."""
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
60
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
61
    def __init__(self, command, cache_mgr, rev_store, verbose=False,
7143.15.2 by Jelmer Vernooij
Run autopep8.
62
                 prune_empty_dirs=True):
7027.2.4 by Jelmer Vernooij
Drop multiple CommitHandler implementations.
63
        super(CommitHandler, self).__init__(command)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
64
        self.cache_mgr = cache_mgr
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
65
        self.rev_store = rev_store
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
66
        self.verbose = verbose
0.64.159 by Ian Clatworthy
make the file-id cache optional and branch-ref aware
67
        self.branch_ref = command.ref
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
68
        self.prune_empty_dirs = prune_empty_dirs
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
69
        # This tracks path->file-id for things we're creating this commit.
70
        # If the same path is created multiple times, we need to warn the
71
        # user and add it just once.
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
72
        # If a path is added then renamed or copied, we need to handle that.
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
73
        self._new_file_ids = {}
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
74
        # This tracks path->file-id for things we're modifying this commit.
75
        # If a path is modified then renamed or copied, we need the make
76
        # sure we grab the new content.
77
        self._modified_file_ids = {}
0.99.13 by Ian Clatworthy
Handle delete then add of a file/symlink in the one commit
78
        # This tracks the paths for things we're deleting this commit.
79
        # If the same path is added or the destination of a rename say,
80
        # then a fresh file-id is required.
81
        self._paths_deleted_this_commit = set()
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
82
0.123.9 by Jelmer Vernooij
Provide stubs for logging functions no longer provided by python-fastimport.
83
    def mutter(self, msg, *args):
84
        """Output a mutter but add context."""
85
        msg = "%s (%s)" % (msg, self.command.id)
86
        mutter(msg, *args)
87
88
    def debug(self, msg, *args):
89
        """Output a mutter if the appropriate -D option was given."""
90
        if "fast-import" in debug.debug_flags:
91
            msg = "%s (%s)" % (msg, self.command.id)
92
            mutter(msg, *args)
93
94
    def note(self, msg, *args):
95
        """Output a note but add context."""
96
        msg = "%s (%s)" % (msg, self.command.id)
97
        note(msg, *args)
98
99
    def warning(self, msg, *args):
100
        """Output a warning but add context."""
101
        msg = "%s (%s)" % (msg, self.command.id)
102
        warning(msg, *args)
103
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
104
    def pre_process_files(self):
105
        """Prepare for committing."""
106
        self.revision_id = self.gen_revision_id()
107
        # cache of texts for this commit, indexed by file-id
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
108
        self.data_for_commit = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
109
        # if self.rev_store.expects_rich_root():
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
110
        self.data_for_commit[inventory.ROOT_ID] = []
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
111
112
        # Track the heads and get the real parent list
0.123.6 by Jelmer Vernooij
Split out reftracker.
113
        parents = self.cache_mgr.reftracker.track_heads(self.command)
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
114
115
        # Convert the parent commit-ids to bzr revision-ids
116
        if parents:
0.129.2 by Jelmer Vernooij
Use lookup functions for committish.
117
            self.parents = [self.cache_mgr.lookup_committish(p)
7143.15.2 by Jelmer Vernooij
Run autopep8.
118
                            for p in parents]
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
119
        else:
120
            self.parents = []
121
        self.debug("%s id: %s, parents: %s", self.command.id,
7143.15.2 by Jelmer Vernooij
Run autopep8.
122
                   self.revision_id, str(self.parents))
0.81.1 by Ian Clatworthy
move GenericCommitHandler into its own module in prep for a delta-based one
123
0.85.2 by Ian Clatworthy
improve per-file graph generation
124
        # Tell the RevisionStore we're starting a new commit
125
        self.revision = self.build_revision()
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
126
        self.parent_invs = [self.get_inventory(p) for p in self.parents]
0.85.2 by Ian Clatworthy
improve per-file graph generation
127
        self.rev_store.start_new_revision(self.revision, self.parents,
7143.15.2 by Jelmer Vernooij
Run autopep8.
128
                                          self.parent_invs)
0.85.2 by Ian Clatworthy
improve per-file graph generation
129
130
        # cache of per-file parents for this commit, indexed by file-id
131
        self.per_file_parents_for_commit = {}
132
        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
133
            self.per_file_parents_for_commit[inventory.ROOT_ID] = ()
0.85.2 by Ian Clatworthy
improve per-file graph generation
134
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
135
        # Keep the basis inventory. This needs to be treated as read-only.
136
        if len(self.parents) == 0:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
137
            self.basis_inventory = self._init_inventory()
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
138
        else:
139
            self.basis_inventory = self.get_inventory(self.parents[0])
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
140
        if hasattr(self.basis_inventory, "root_id"):
141
            self.inventory_root_id = self.basis_inventory.root_id
142
        else:
143
            self.inventory_root_id = self.basis_inventory.root.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
144
145
        # directory-path -> inventory-entry for current inventory
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
146
        self.directory_entries = {}
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
147
7027.2.4 by Jelmer Vernooij
Drop multiple CommitHandler implementations.
148
        self._dirs_that_might_become_empty = set()
149
150
        # A given file-id can only appear once so we accumulate
151
        # the entries in a dict then build the actual delta at the end
152
        self._delta_entries_by_fileid = {}
153
        if len(self.parents) == 0 or not self.rev_store.expects_rich_root():
154
            if self.parents:
155
                old_path = ''
156
            else:
157
                old_path = None
158
            # Need to explicitly add the root entry for the first revision
159
            # and for non rich-root inventories
160
            root_id = inventory.ROOT_ID
161
            root_ie = inventory.InventoryDirectory(root_id, u'', None)
162
            root_ie.revision = self.revision_id
163
            self._add_entry((old_path, '', root_id, root_ie))
164
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
165
    def _init_inventory(self):
166
        return self.rev_store.init_inventory(self.revision_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
167
168
    def get_inventory(self, revision_id):
169
        """Get the inventory for a revision id."""
170
        try:
171
            inv = self.cache_mgr.inventories[revision_id]
172
        except KeyError:
173
            if self.verbose:
0.64.148 by Ian Clatworthy
handle delete of unknown file in chk formats & reduce noise
174
                self.mutter("get_inventory cache miss for %s", revision_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
175
            # Not cached so reconstruct from the RevisionStore
176
            inv = self.rev_store.get_inventory(revision_id)
177
            self.cache_mgr.inventories[revision_id] = inv
178
        return inv
179
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
180
    def _get_data(self, file_id):
181
        """Get the data bytes for a file-id."""
182
        return self.data_for_commit[file_id]
183
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
184
    def _get_lines(self, file_id):
185
        """Get the lines for a file-id."""
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
186
        return osutils.split_lines(self._get_data(file_id))
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
187
0.85.2 by Ian Clatworthy
improve per-file graph generation
188
    def _get_per_file_parents(self, file_id):
189
        """Get the lines for a file-id."""
190
        return self.per_file_parents_for_commit[file_id]
191
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
192
    def _get_inventories(self, revision_ids):
193
        """Get the inventories for revision-ids.
7143.15.2 by Jelmer Vernooij
Run autopep8.
194
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
195
        This is a callback used by the RepositoryStore to
196
        speed up inventory reconstruction.
197
        """
198
        present = []
199
        inventories = []
200
        # If an inventory is in the cache, we assume it was
201
        # successfully loaded into the revision store
202
        for revision_id in revision_ids:
203
            try:
204
                inv = self.cache_mgr.inventories[revision_id]
205
                present.append(revision_id)
206
            except KeyError:
207
                if self.verbose:
208
                    self.note("get_inventories cache miss for %s", revision_id)
209
                # Not cached so reconstruct from the revision store
210
                try:
211
                    inv = self.get_inventory(revision_id)
212
                    present.append(revision_id)
213
                except:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
214
                    inv = self._init_inventory()
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
215
                self.cache_mgr.inventories[revision_id] = inv
216
            inventories.append(inv)
217
        return present, inventories
218
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
219
    def bzr_file_id_and_new(self, path):
220
        """Get a Bazaar file identifier and new flag for a path.
7143.15.2 by Jelmer Vernooij
Run autopep8.
221
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
222
        :return: file_id, is_new where
223
          is_new = True if the file_id is newly created
224
        """
0.99.13 by Ian Clatworthy
Handle delete then add of a file/symlink in the one commit
225
        if path not in self._paths_deleted_this_commit:
0.99.19 by Ian Clatworthy
Handle rename then modification of the new path
226
            # Try file-ids renamed in this commit
227
            id = self._modified_file_ids.get(path)
228
            if id is not None:
229
                return id, False
230
0.99.13 by Ian Clatworthy
Handle delete then add of a file/symlink in the one commit
231
            # Try the basis inventory
232
            id = self.basis_inventory.path2id(path)
233
            if id is not None:
234
                return id, False
7143.15.2 by Jelmer Vernooij
Run autopep8.
235
0.99.13 by Ian Clatworthy
Handle delete then add of a file/symlink in the one commit
236
            # Try the other inventories
237
            if len(self.parents) > 1:
238
                for inv in self.parent_invs[1:]:
239
                    id = self.basis_inventory.path2id(path)
240
                    if id is not None:
241
                        return id, False
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
242
243
        # Doesn't exist yet so create it
0.64.247 by Ian Clatworthy
base file-ids on the basename, not path, as jam suggested. This improves the samba import from 565M to 353M.
244
        dirname, basename = osutils.split(path)
245
        id = generate_ids.gen_file_id(basename)
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
246
        self.debug("Generated new file id %s for '%s' in revision-id '%s'",
7143.15.2 by Jelmer Vernooij
Run autopep8.
247
                   id, path, self.revision_id)
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
248
        self._new_file_ids[path] = id
0.99.1 by Ian Clatworthy
lookup file-ids in inventories instead of a cache
249
        return id, True
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
250
251
    def bzr_file_id(self, path):
252
        """Get a Bazaar file identifier for a path."""
253
        return self.bzr_file_id_and_new(path)[0]
254
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
255
    def _utf8_decode(self, field, value):
256
        try:
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
257
            return value.decode('utf-8')
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
258
        except UnicodeDecodeError:
259
            # The spec says fields are *typically* utf8 encoded
260
            # but that isn't enforced by git-fast-export (at least)
261
            self.warning("%s not in utf8 - replacing unknown "
7143.15.2 by Jelmer Vernooij
Run autopep8.
262
                         "characters" % (field,))
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
263
            return value.decode('utf-8', 'replace')
264
265
    def _decode_path(self, path):
266
        try:
267
            return path.decode('utf-8')
268
        except UnicodeDecodeError:
269
            # The spec says fields are *typically* utf8 encoded
270
            # but that isn't enforced by git-fast-export (at least)
271
            self.warning("path %r not in utf8 - replacing unknown "
7143.15.2 by Jelmer Vernooij
Run autopep8.
272
                         "characters" % (path,))
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
273
            return path.decode('utf-8', 'replace')
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
274
275
    def _format_name_email(self, section, name, email):
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
276
        """Format name & email as a string."""
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
277
        name = self._utf8_decode("%s name" % section, name)
278
        email = self._utf8_decode("%s email" % section, email)
279
0.64.177 by Ian Clatworthy
fix round-tripping of committer & author when name is an email
280
        if email:
281
            return "%s <%s>" % (name, email)
282
        else:
283
            return name
284
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
285
    def gen_revision_id(self):
286
        """Generate a revision id.
287
288
        Subclasses may override this to produce deterministic ids say.
289
        """
290
        committer = self.command.committer
291
        # Perhaps 'who' being the person running the import is ok? If so,
292
        # it might be a bit quicker and give slightly better compression?
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
293
        who = self._format_name_email("committer", committer[0], committer[1])
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
294
        timestamp = committer[2]
295
        return generate_ids.gen_revision_id(who, timestamp)
296
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
297
    def build_revision(self):
0.64.235 by Ian Clatworthy
Sanitize None revision properties to empty string
298
        rev_props = self._legal_revision_properties(self.command.properties)
6973.12.5 by Jelmer Vernooij
Add some u's for revision property names.
299
        if u'branch-nick' not in rev_props:
300
            rev_props[u'branch-nick'] = self.cache_mgr.branch_mapper.git_to_bzr(
7143.15.2 by Jelmer Vernooij
Run autopep8.
301
                self.branch_ref)
0.102.10 by Ian Clatworthy
Store multiple authors and revision properties when defined
302
        self._save_author_info(rev_props)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
303
        committer = self.command.committer
0.64.299 by Jelmer Vernooij
utf8 decode/encode paths and committer/author email/name, as python-fastimport no longer does so.
304
        who = self._format_name_email("committer", committer[0], committer[1])
0.64.298 by Jelmer Vernooij
Handle unicode decoding of commit messages in bzr-fastimport, python-fastimport no longer takes care of this.
305
        try:
306
            message = self.command.message.decode("utf-8")
0.64.303 by Jelmer Vernooij
Cope with non-utf8 characters in commit messages.
307
0.64.298 by Jelmer Vernooij
Handle unicode decoding of commit messages in bzr-fastimport, python-fastimport no longer takes care of this.
308
        except UnicodeDecodeError:
309
            self.warning(
310
                "commit message not in utf8 - replacing unknown characters")
0.64.303 by Jelmer Vernooij
Cope with non-utf8 characters in commit messages.
311
            message = self.command.message.decode('utf-8', 'replace')
0.64.192 by Ian Clatworthy
delegate commit message escaping to the serializer if it's a modern one
312
        if not _serializer_handles_escaping:
313
            # We need to assume the bad ol' days
314
            message = helpers.escape_commit_message(message)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
315
        return revision.Revision(
7143.15.2 by Jelmer Vernooij
Run autopep8.
316
            timestamp=committer[2],
317
            timezone=committer[3],
318
            committer=who,
319
            message=message,
320
            revision_id=self.revision_id,
321
            properties=rev_props,
322
            parent_ids=self.parents)
0.81.2 by Ian Clatworthy
refactor InventoryCommitHandler general stuff into parent class
323
0.64.235 by Ian Clatworthy
Sanitize None revision properties to empty string
324
    def _legal_revision_properties(self, props):
325
        """Clean-up any revision properties we can't handle."""
326
        # For now, we just check for None because that's not allowed in 2.0rc1
327
        result = {}
328
        if props is not None:
329
            for name, value in props.items():
330
                if value is None:
331
                    self.warning(
332
                        "converting None to empty string for property %s"
333
                        % (name,))
334
                    result[name] = ''
335
                else:
336
                    result[name] = value
337
        return result
338
0.102.10 by Ian Clatworthy
Store multiple authors and revision properties when defined
339
    def _save_author_info(self, rev_props):
340
        author = self.command.author
341
        if author is None:
342
            return
343
        if self.command.more_authors:
344
            authors = [author] + self.command.more_authors
7143.15.2 by Jelmer Vernooij
Run autopep8.
345
            author_ids = [self._format_name_email(
346
                "author", a[0], a[1]) for a in authors]
0.102.10 by Ian Clatworthy
Store multiple authors and revision properties when defined
347
        elif author != self.command.committer:
7143.15.2 by Jelmer Vernooij
Run autopep8.
348
            author_ids = [self._format_name_email(
349
                "author", author[0], author[1])]
0.102.10 by Ian Clatworthy
Store multiple authors and revision properties when defined
350
        else:
351
            return
352
        # If we reach here, there are authors worth storing
6973.12.3 by Jelmer Vernooij
Fixes.
353
        rev_props[u'authors'] = "\n".join(author_ids)
0.102.10 by Ian Clatworthy
Store multiple authors and revision properties when defined
354
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
355
    def _modify_item(self, path, kind, is_executable, data, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
356
        """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
357
        # If we've already added this, warn the user that we're ignoring it.
358
        # In the future, it might be nice to double check that the new data
359
        # is the same as the old but, frankly, exporters should be fixed
360
        # not to produce bad data streams in the first place ...
361
        existing = self._new_file_ids.get(path)
362
        if existing:
0.102.18 by Ian Clatworthy
Tweak some diagnostic messages
363
            # We don't warn about directories because it's fine for them
364
            # to be created already by a previous rename
365
            if kind != 'directory':
366
                self.warning("%s already added in this commit - ignoring" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
367
                             (path,))
0.99.5 by Ian Clatworthy
handle adding the same file twice in the one commit
368
            return
369
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
370
        # Create the new InventoryEntry
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
371
        basename, parent_id = self._ensure_directory(path, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
372
        file_id = self.bzr_file_id(path)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
373
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
374
        ie.revision = self.revision_id
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
375
        if kind == 'file':
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
376
            ie.executable = is_executable
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
377
            # lines = osutils.split_lines(data)
378
            ie.text_sha1 = osutils.sha_string(data)
379
            ie.text_size = len(data)
380
            self.data_for_commit[file_id] = data
0.102.14 by Ian Clatworthy
export and import empty directories
381
        elif kind == 'directory':
382
            self.directory_entries[path] = ie
383
            # There are no lines stored for a directory so
384
            # make sure the cache used by get_lines knows that
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
385
            self.data_for_commit[file_id] = b''
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
386
        elif kind == 'symlink':
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
387
            ie.symlink_target = self._decode_path(data)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
388
            # There are no lines stored for a symlink so
389
            # make sure the cache used by get_lines knows that
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
390
            self.data_for_commit[file_id] = b''
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
391
        else:
0.64.229 by Ian Clatworthy
Handle git submodules in the stream by warning about + ignoring them
392
            self.warning("Cannot import items of kind '%s' yet - ignoring '%s'"
7143.15.2 by Jelmer Vernooij
Run autopep8.
393
                         % (kind, path))
0.64.229 by Ian Clatworthy
Handle git submodules in the stream by warning about + ignoring them
394
            return
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
395
        # Record it
6883.7.11 by Jelmer Vernooij
Avoid has_id.
396
        try:
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
397
            old_ie = inv.get_entry(file_id)
6883.7.11 by Jelmer Vernooij
Avoid has_id.
398
        except errors.NoSuchId:
0.64.165 by Ian Clatworthy
handle adding a file to a dir deleted in the same commit
399
            try:
400
                self.record_new(path, ie)
401
            except:
7143.15.2 by Jelmer Vernooij
Run autopep8.
402
                print("failed to add path '%s' with entry '%s' in command %s"
403
                      % (path, ie, self.command.id))
404
                print("parent's children are:\n%r\n" %
405
                      (ie.parent_id.children,))
0.64.165 by Ian Clatworthy
handle adding a file to a dir deleted in the same commit
406
                raise
6883.7.11 by Jelmer Vernooij
Avoid has_id.
407
        else:
408
            if old_ie.kind == 'directory':
409
                self.record_delete(path, old_ie)
410
            self.record_changed(path, ie, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
411
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
412
    def _ensure_directory(self, path, inv):
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
413
        """Ensure that the containing directory exists for 'path'"""
414
        dirname, basename = osutils.split(path)
415
        if dirname == '':
416
            # the root node doesn't get updated
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
417
            return basename, self.inventory_root_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
418
        try:
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
419
            ie = self._get_directory_entry(inv, dirname)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
420
        except KeyError:
421
            # We will create this entry, since it doesn't exist
422
            pass
423
        else:
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
424
            return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
425
426
        # No directory existed, we will just create one, first, make sure
427
        # the parent exists
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
428
        dir_basename, parent_id = self._ensure_directory(dirname, inv)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
429
        dir_file_id = self.bzr_file_id(dirname)
430
        ie = inventory.entry_factory['directory'](dir_file_id,
7143.15.2 by Jelmer Vernooij
Run autopep8.
431
                                                  dir_basename, parent_id)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
432
        ie.revision = self.revision_id
433
        self.directory_entries[dirname] = ie
434
        # There are no lines stored for a directory so
435
        # make sure the cache used by get_lines knows that
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
436
        self.data_for_commit[dir_file_id] = b''
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
437
438
        # It's possible that a file or symlink with that file-id
439
        # already exists. If it does, we need to delete it.
0.64.323 by Jelmer Vernooij
Avoid deprecated Inventory.__contains__.
440
        if inv.has_id(dir_file_id):
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
441
            self.record_delete(dirname, ie)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
442
        self.record_new(dirname, ie)
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
443
        return basename, ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
444
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
445
    def _get_directory_entry(self, inv, dirname):
446
        """Get the inventory entry for a directory.
7143.15.2 by Jelmer Vernooij
Run autopep8.
447
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
448
        Raises KeyError if dirname is not a directory in inv.
449
        """
450
        result = self.directory_entries.get(dirname)
451
        if result is None:
0.99.21 by Ian Clatworthy
Handle deleting a directory then adding a file within it in the same commit
452
            if dirname in self._paths_deleted_this_commit:
453
                raise KeyError
0.64.146 by Ian Clatworthy
fix first file is in a subdirectory bug for chk formats
454
            try:
455
                file_id = inv.path2id(dirname)
456
            except errors.NoSuchId:
457
                # In a CHKInventory, this is raised if there's no root yet
458
                raise KeyError
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
459
            if file_id is None:
460
                raise KeyError
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
461
            result = inv.get_entry(file_id)
0.84.12 by Ian Clatworthy
lookup directories on demand in CHKInventories, not all upfront
462
            # dirname must be a directory for us to return it
463
            if result.kind == 'directory':
464
                self.directory_entries[dirname] = result
465
            else:
466
                raise KeyError
467
        return result
468
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
469
    def _delete_item(self, path, inv):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
470
        newly_added = self._new_file_ids.get(path)
471
        if newly_added:
472
            # We've only just added this path earlier in this commit.
473
            file_id = newly_added
474
            # note: delta entries look like (old, new, file-id, ie)
475
            ie = self._delta_entries_by_fileid[file_id][3]
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
476
        else:
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
477
            file_id = inv.path2id(path)
478
            if file_id is None:
479
                self.mutter("ignoring delete of %s as not in inventory", path)
480
                return
481
            try:
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
482
                ie = inv.get_entry(file_id)
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
483
            except errors.NoSuchId:
484
                self.mutter("ignoring delete of %s as not in inventory", path)
485
                return
486
        self.record_delete(path, ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
487
488
    def _copy_item(self, src_path, dest_path, inv):
0.99.18 by Ian Clatworthy
Handle copy of a file/symlink already modified in this commit
489
        newly_changed = self._new_file_ids.get(src_path) or \
490
            self._modified_file_ids.get(src_path)
491
        if newly_changed:
492
            # We've only just added/changed this path earlier in this commit.
493
            file_id = newly_changed
0.99.8 by Ian Clatworthy
handle copy of a newly added file
494
            # note: delta entries look like (old, new, file-id, ie)
495
            ie = self._delta_entries_by_fileid[file_id][3]
496
        else:
497
            file_id = inv.path2id(src_path)
498
            if file_id is None:
499
                self.warning("ignoring copy of %s to %s - source does not exist",
7143.15.2 by Jelmer Vernooij
Run autopep8.
500
                             src_path, dest_path)
0.99.8 by Ian Clatworthy
handle copy of a newly added file
501
                return
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
502
            ie = inv.get_entry(file_id)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
503
        kind = ie.kind
504
        if kind == 'file':
0.99.18 by Ian Clatworthy
Handle copy of a file/symlink already modified in this commit
505
            if newly_changed:
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
506
                content = self.data_for_commit[file_id]
0.99.8 by Ian Clatworthy
handle copy of a newly added file
507
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
508
                content = self.rev_store.get_file_text(
509
                    self.parents[0], file_id)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
510
            self._modify_item(dest_path, kind, ie.executable, content, inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
511
        elif kind == 'symlink':
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
512
            self._modify_item(dest_path, kind, False,
7143.15.2 by Jelmer Vernooij
Run autopep8.
513
                              ie.symlink_target.encode("utf-8"), inv)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
514
        else:
515
            self.warning("ignoring copy of %s %s - feature not yet supported",
7143.15.2 by Jelmer Vernooij
Run autopep8.
516
                         kind, dest_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
517
518
    def _rename_item(self, old_path, new_path, inv):
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
519
        existing = self._new_file_ids.get(old_path) or \
520
            self._modified_file_ids.get(old_path)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
521
        if existing:
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
522
            # We've only just added/modified this path earlier in this commit.
523
            # Change the add/modify of old_path to an add of new_path
524
            self._rename_pending_change(old_path, new_path, existing)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
525
            return
526
0.81.8 by Ian Clatworthy
refactor rename_item
527
        file_id = inv.path2id(old_path)
0.64.167 by Ian Clatworthy
incremental packing for chk formats
528
        if file_id is None:
529
            self.warning(
530
                "ignoring rename of %s to %s - old path does not exist" %
531
                (old_path, new_path))
532
            return
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
533
        ie = inv.get_entry(file_id)
0.81.8 by Ian Clatworthy
refactor rename_item
534
        rev_id = ie.revision
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
535
        new_file_id = inv.path2id(new_path)
536
        if new_file_id is not None:
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
537
            self.record_delete(new_path, inv.get_entry(new_file_id))
0.81.8 by Ian Clatworthy
refactor rename_item
538
        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
539
0.81.8 by Ian Clatworthy
refactor rename_item
540
        # 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
541
        # that means the loader then needs to know what the "new" text is.
542
        # We therefore must go back to the revision store to get it.
0.81.8 by Ian Clatworthy
refactor rename_item
543
        lines = self.rev_store.get_file_lines(rev_id, file_id)
7027.2.1 by Jelmer Vernooij
Port fastimport to python3.
544
        self.data_for_commit[file_id] = b''.join(lines)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
545
546
    def _delete_all_items(self, inv):
0.64.320 by Jelmer Vernooij
Fix deleteall handler.
547
        if len(inv) == 0:
548
            return
549
        for path, ie in inv.iter_entries_by_dir():
550
            if path != "":
551
                self.record_delete(path, ie)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
552
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
553
    def _warn_unless_in_merges(self, fileid, path):
554
        if len(self.parents) <= 1:
555
            return
556
        for parent in self.parents[1:]:
557
            if fileid in self.get_inventory(parent):
558
                return
7143.15.2 by Jelmer Vernooij
Run autopep8.
559
        self.warning(
560
            "ignoring delete of %s as not in parent inventories", path)
0.64.145 by Ian Clatworthy
handle delete of missing files for chk formats
561
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
562
    def post_process_files(self):
563
        """Save the revision."""
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
564
        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
565
        inv = self.rev_store.load_using_delta(self.revision,
7143.15.2 by Jelmer Vernooij
Run autopep8.
566
                                              self.basis_inventory, delta, None,
567
                                              self._get_data,
568
                                              self._get_per_file_parents,
569
                                              self._get_inventories)
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
570
        self.cache_mgr.inventories[self.revision_id] = inv
7143.15.2 by Jelmer Vernooij
Run autopep8.
571
        # print "committed %s" % self.revision_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
572
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
573
    def _get_final_delta(self):
574
        """Generate the final delta.
575
576
        Smart post-processing of changes, e.g. pruning of directories
577
        that would become empty, goes here.
578
        """
579
        delta = list(self._delta_entries_by_fileid.values())
580
        if self.prune_empty_dirs and self._dirs_that_might_become_empty:
0.101.2 by Tom Widmer
Update pruning code to operate in multiple passes, with subsequent passes operating on the parent dirs of dirs pruned in the previous pass.
581
            candidates = self._dirs_that_might_become_empty
582
            while candidates:
583
                never_born = set()
584
                parent_dirs_that_might_become_empty = set()
585
                for path, file_id in self._empty_after_delta(delta, candidates):
586
                    newly_added = self._new_file_ids.get(path)
587
                    if newly_added:
588
                        never_born.add(newly_added)
589
                    else:
590
                        delta.append((path, None, file_id, None))
591
                    parent_dir = osutils.dirname(path)
592
                    if parent_dir:
593
                        parent_dirs_that_might_become_empty.add(parent_dir)
594
                candidates = parent_dirs_that_might_become_empty
0.101.5 by Tom Widmer
Add missing tab characters to ensure that never born dirs are correctly removed during each pass of parent directory pruning.
595
                # Clean up entries that got deleted before they were ever added
596
                if never_born:
597
                    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
598
        return delta
599
600
    def _empty_after_delta(self, delta, candidates):
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
601
        #self.mutter("delta so far is:\n%s" % "\n".join([str(de) for de in delta]))
602
        #self.mutter("candidates for deletion are:\n%s" % "\n".join([c for c in candidates]))
603
        new_inv = self._get_proposed_inventory(delta)
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
604
        result = []
605
        for dir in candidates:
606
            file_id = new_inv.path2id(dir)
0.64.219 by Ian Clatworthy
More robust implicit delete logic when file-id not found
607
            if file_id is None:
608
                continue
6915.4.2 by Jelmer Vernooij
Remove __getitem__ and __iter__ from Inventory.
609
            ie = new_inv.get_entry(file_id)
0.101.2 by Tom Widmer
Update pruning code to operate in multiple passes, with subsequent passes operating on the parent dirs of dirs pruned in the previous pass.
610
            if ie.kind != 'directory':
611
                continue
0.96.2 by Ian Clatworthy
test and fix for implicit directory delete recursing up
612
            if len(ie.children) == 0:
613
                result.append((dir, file_id))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
614
                if self.verbose:
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
615
                    self.note("pruning empty directory %s" % (dir,))
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
616
        return result
617
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
618
    def _get_proposed_inventory(self, delta):
619
        if len(self.parents):
0.114.1 by John Arbash Meinel
When post-processing the delta stream, don't ask to generate a full inventory to check for deletions.
620
            # new_inv = self.basis_inventory._get_mutable_inventory()
621
            # Note that this will create unreferenced chk pages if we end up
622
            # deleting entries, because this 'test' inventory won't end up
623
            # used. However, it is cheaper than having to create a full copy of
624
            # the inventory for every commit.
625
            new_inv = self.basis_inventory.create_by_apply_delta(delta,
7143.15.2 by Jelmer Vernooij
Run autopep8.
626
                                                                 b'not-a-valid-revision-id:')
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
627
        else:
628
            new_inv = inventory.Inventory(revision_id=self.revision_id)
629
            # This is set in the delta so remove it to prevent a duplicate
6915.4.1 by Jelmer Vernooij
Inventory.__delitem__ => Inventory.delete.
630
            new_inv.delete(inventory.ROOT_ID)
0.114.1 by John Arbash Meinel
When post-processing the delta stream, don't ask to generate a full inventory to check for deletions.
631
            try:
632
                new_inv.apply_delta(delta)
633
            except errors.InconsistentDelta:
7143.15.2 by Jelmer Vernooij
Run autopep8.
634
                self.mutter("INCONSISTENT DELTA IS:\n%s" %
635
                            "\n".join([str(de) for de in delta]))
0.114.1 by John Arbash Meinel
When post-processing the delta stream, don't ask to generate a full inventory to check for deletions.
636
                raise
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
637
        return new_inv
638
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
639
    def _add_entry(self, entry):
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
640
        # 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
641
        # For example, a rename followed by a modification looks like:
642
        #
643
        # (x, y, f, e) & (y, y, f, g) => (x, y, f, g)
644
        #
645
        # Likewise, a modification followed by a rename looks like:
646
        #
647
        # (x, x, f, e) & (x, y, f, g) => (x, y, f, g)
648
        #
649
        # Here's a rename followed by a delete and a modification followed by
650
        # a delete:
651
        #
652
        # (x, y, f, e) & (y, None, f, None) => (x, None, f, None)
653
        # (x, x, f, e) & (x, None, f, None) => (x, None, f, None)
654
        #
655
        # In summary, we use the original old-path, new new-path and new ie
656
        # when combining entries.
0.85.2 by Ian Clatworthy
improve per-file graph generation
657
        old_path = entry[0]
658
        new_path = entry[1]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
659
        file_id = entry[2]
0.85.2 by Ian Clatworthy
improve per-file graph generation
660
        ie = entry[3]
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
661
        existing = self._delta_entries_by_fileid.get(file_id, None)
662
        if existing is not None:
0.85.2 by Ian Clatworthy
improve per-file graph generation
663
            old_path = existing[0]
664
            entry = (old_path, new_path, file_id, ie)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
665
        if new_path is None and old_path is None:
666
            # This is a delete cancelling a previous add
667
            del self._delta_entries_by_fileid[file_id]
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
668
            parent_dir = osutils.dirname(existing[1])
7143.15.2 by Jelmer Vernooij
Run autopep8.
669
            self.mutter("cancelling add of %s with parent %s" %
670
                        (existing[1], parent_dir))
0.99.7 by Ian Clatworthy
handle a delete of a newly added file
671
            if parent_dir:
672
                self._dirs_that_might_become_empty.add(parent_dir)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
673
            return
674
        else:
675
            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
676
0.99.6 by Ian Clatworthy
Handle rename of a just added file
677
        # Collect parent directories that might become empty
0.64.195 by Ian Clatworthy
prune directories that become empty after a delete or rename
678
        if new_path is None:
679
            # delete
680
            parent_dir = osutils.dirname(old_path)
681
            # note: no need to check the root
682
            if parent_dir:
683
                self._dirs_that_might_become_empty.add(parent_dir)
684
        elif old_path is not None and old_path != new_path:
685
            # rename
686
            old_parent_dir = osutils.dirname(old_path)
687
            new_parent_dir = osutils.dirname(new_path)
688
            if old_parent_dir and old_parent_dir != new_parent_dir:
689
                self._dirs_that_might_become_empty.add(old_parent_dir)
690
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
691
        # Calculate the per-file parents, if not already done
692
        if file_id in self.per_file_parents_for_commit:
693
            return
0.85.2 by Ian Clatworthy
improve per-file graph generation
694
        if old_path is None:
695
            # add
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
696
            # If this is a merge, the file was most likely added already.
697
            # The per-file parent(s) must therefore be calculated and
698
            # we can't assume there are none.
699
            per_file_parents, ie.revision = \
700
                self.rev_store.get_parents_and_revision_for_entry(ie)
701
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
702
        elif new_path is None:
703
            # delete
704
            pass
705
        elif old_path != new_path:
706
            # rename
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
707
            per_file_parents, _ = \
708
                self.rev_store.get_parents_and_revision_for_entry(ie)
709
            self.per_file_parents_for_commit[file_id] = per_file_parents
0.85.2 by Ian Clatworthy
improve per-file graph generation
710
        else:
711
            # modify
712
            per_file_parents, ie.revision = \
713
                self.rev_store.get_parents_and_revision_for_entry(ie)
714
            self.per_file_parents_for_commit[file_id] = per_file_parents
715
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
716
    def record_new(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
717
        self._add_entry((None, path, ie.file_id, ie))
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
718
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
719
    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
720
        self._add_entry((path, path, ie.file_id, ie))
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
721
        self._modified_file_ids[path] = ie.file_id
0.81.5 by Ian Clatworthy
basic DeltaCommitHandler generating deltas
722
0.81.9 by Ian Clatworthy
refactor delete_item
723
    def record_delete(self, path, ie):
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
724
        self._add_entry((path, None, ie.file_id, None))
0.99.13 by Ian Clatworthy
Handle delete then add of a file/symlink in the one commit
725
        self._paths_deleted_this_commit.add(path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
726
        if ie.kind == 'directory':
0.99.21 by Ian Clatworthy
Handle deleting a directory then adding a file within it in the same commit
727
            try:
728
                del self.directory_entries[path]
729
            except KeyError:
730
                pass
7045.1.8 by Jelmer Vernooij
fix some tests
731
            if self.basis_inventory.get_entry(ie.file_id).kind == 'directory':
732
                for child_relpath, entry in \
7143.15.2 by Jelmer Vernooij
Run autopep8.
733
                        self.basis_inventory.iter_entries_by_dir(from_dir=ie.file_id):
7045.1.8 by Jelmer Vernooij
fix some tests
734
                    child_path = osutils.pathjoin(path, child_relpath)
735
                    self._add_entry((child_path, None, entry.file_id, None))
736
                    self._paths_deleted_this_commit.add(child_path)
737
                    if entry.kind == 'directory':
738
                        try:
739
                            del self.directory_entries[child_path]
740
                        except KeyError:
741
                            pass
0.81.8 by Ian Clatworthy
refactor rename_item
742
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
743
    def record_rename(self, old_path, new_path, file_id, old_ie):
744
        new_ie = old_ie.copy()
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
745
        new_basename, new_parent_id = self._ensure_directory(new_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
746
                                                             self.basis_inventory)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
747
        new_ie.name = new_basename
0.84.5 by Ian Clatworthy
_ensure_directory to return parent_id, not parent_ie
748
        new_ie.parent_id = new_parent_id
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
749
        new_ie.revision = self.revision_id
0.84.9 by Ian Clatworthy
get non-chk formats working again & combine delta entries when required
750
        self._add_entry((old_path, new_path, file_id, new_ie))
0.99.19 by Ian Clatworthy
Handle rename then modification of the new path
751
        self._modified_file_ids[new_path] = file_id
0.64.233 by Ian Clatworthy
Handle delete, rename then modify all in the one commit
752
        self._paths_deleted_this_commit.discard(new_path)
0.64.234 by Ian Clatworthy
Make sure renamed directories are found in file-id lookups
753
        if new_ie.kind == 'directory':
754
            self.directory_entries[new_path] = new_ie
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
755
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
756
    def _rename_pending_change(self, old_path, new_path, file_id):
757
        """Instead of adding/modifying old-path, add new-path instead."""
0.99.6 by Ian Clatworthy
Handle rename of a just added file
758
        # note: delta entries look like (old, new, file-id, ie)
759
        old_ie = self._delta_entries_by_fileid[file_id][3]
760
761
        # Delete the old path. Note that this might trigger implicit
762
        # deletion of newly created parents that could now become empty.
763
        self.record_delete(old_path, old_ie)
764
0.99.17 by Ian Clatworthy
Handle rename of a file/symlink modified already in this commit
765
        # Update the dictionaries used for tracking new file-ids
766
        if old_path in self._new_file_ids:
767
            del self._new_file_ids[old_path]
768
        else:
769
            del self._modified_file_ids[old_path]
0.99.6 by Ian Clatworthy
Handle rename of a just added file
770
        self._new_file_ids[new_path] = file_id
771
772
        # Create the new InventoryEntry
773
        kind = old_ie.kind
774
        basename, parent_id = self._ensure_directory(new_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
775
                                                     self.basis_inventory)
0.99.6 by Ian Clatworthy
Handle rename of a just added file
776
        ie = inventory.make_entry(kind, basename, parent_id, file_id)
777
        ie.revision = self.revision_id
778
        if kind == 'file':
779
            ie.executable = old_ie.executable
780
            ie.text_sha1 = old_ie.text_sha1
781
            ie.text_size = old_ie.text_size
782
        elif kind == 'symlink':
783
            ie.symlink_target = old_ie.symlink_target
784
785
        # Record it
786
        self.record_new(new_path, ie)
787
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
788
    def modify_handler(self, filecmd):
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
789
        (kind, executable) = mode_to_kind(filecmd.mode)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
790
        if filecmd.dataref is not None:
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
791
            if kind == "directory":
0.102.14 by Ian Clatworthy
export and import empty directories
792
                data = None
0.123.8 by Jelmer Vernooij
Use modes for FileModifyCommand.
793
            elif kind == "tree-reference":
0.64.229 by Ian Clatworthy
Handle git submodules in the stream by warning about + ignoring them
794
                data = filecmd.dataref
795
            else:
796
                data = self.cache_mgr.fetch_blob(filecmd.dataref)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
797
        else:
798
            data = filecmd.data
799
        self.debug("modifying %s", filecmd.path)
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
800
        decoded_path = self._decode_path(filecmd.path)
801
        self._modify_item(decoded_path, kind,
7143.15.2 by Jelmer Vernooij
Run autopep8.
802
                          executable, data, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
803
804
    def delete_handler(self, filecmd):
805
        self.debug("deleting %s", filecmd.path)
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
806
        self._delete_item(
807
            self._decode_path(filecmd.path), self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
808
809
    def copy_handler(self, filecmd):
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
810
        src_path = self._decode_path(filecmd.src_path)
811
        dest_path = self._decode_path(filecmd.dest_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
812
        self.debug("copying %s to %s", src_path, dest_path)
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
813
        self._copy_item(src_path, dest_path, self.basis_inventory)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
814
815
    def rename_handler(self, filecmd):
0.64.332 by Jelmer Vernooij
Cope with non-utf8 characters in paths when importing.
816
        old_path = self._decode_path(filecmd.old_path)
817
        new_path = self._decode_path(filecmd.new_path)
0.81.6 by Ian Clatworthy
basic DeltaCommitHandler mostly going bar rename
818
        self.debug("renaming %s to %s", old_path, new_path)
819
        self._rename_item(old_path, new_path, self.basis_inventory)
820
821
    def deleteall_handler(self, filecmd):
822
        self.debug("deleting all files (and also all directories)")
823
        self._delete_all_items(self.basis_inventory)