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