~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-10-03 05:04:42 UTC
  • mfrom: (2776.4.19 commit)
  • Revision ID: pqm@pqm.ubuntu.com-20071003050442-e0x9ofdfo0hwxnal
(robertc) Move serialisation of inventory entries into CommitBuilder. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
200
200
                ' record_entry_contents, as of bzr 0.10.',
201
201
                 DeprecationWarning, stacklevel=2)
202
202
            self.record_entry_contents(tree.inventory.root.copy(), parent_invs,
203
 
                                       '', tree)
 
203
                                       '', tree, tree.path_content_summary(''))
204
204
        else:
205
205
            # In this revision format, root entries have no knit or weave When
206
206
            # serializing out to disk and back in root.revision is always
207
207
            # _new_revision_id
208
208
            ie.revision = self._new_revision_id
209
209
 
210
 
    def record_entry_contents(self, ie, parent_invs, path, tree):
 
210
    def record_entry_contents(self, ie, parent_invs, path, tree,
 
211
        content_summary):
211
212
        """Record the content of ie from tree into the commit if needed.
212
213
 
213
214
        Side effect: sets ie.revision when unchanged
217
218
            commit.
218
219
        :param path: The path the entry is at in the tree.
219
220
        :param tree: The tree which contains this entry and should be used to 
220
 
        obtain content.
 
221
            obtain content.
 
222
        :param content_summary: Summary data from the tree about the paths
 
223
            content - stat, length, exec, sha/link target. This is only
 
224
            accessed when the entry has a revision of None - that is when it is
 
225
            a candidate to commit.
221
226
        :return: True if a new version of the entry has been recorded.
222
227
            (Committing a merge where a file was only changed on the other side
223
228
            will not return True.)
224
229
        """
225
230
        if self.new_inventory.root is None:
226
231
            self._check_root(ie, parent_invs, tree)
 
232
        if ie.revision is None:
 
233
            kind = content_summary[0]
 
234
        else:
 
235
            # ie is carried over from a prior commit
 
236
            kind = ie.kind
 
237
        # XXX: repository specific check for nested tree support goes here - if
 
238
        # the repo doesn't want nested trees we skip it ?
 
239
        if (kind == 'tree-reference' and
 
240
            not self.repository._format.supports_tree_reference):
 
241
            # mismatch between commit builder logic and repository:
 
242
            # this needs the entry creation pushed down into the builder.
 
243
            raise NotImplementedError('Missing repository subtree support.')
 
244
        # transitional assert only, will remove before release.
 
245
        assert ie.kind == kind
227
246
        self.new_inventory.add(ie)
228
247
 
229
248
        # ie.revision is always None if the InventoryEntry is considered
230
 
        # for committing. ie.snapshot will record the correct revision 
231
 
        # which may be the sole parent if it is untouched.
 
249
        # for committing. We may record the previous parents revision if the
 
250
        # content is actually unchanged against a sole head.
232
251
        if ie.revision is not None:
233
252
            return ie.revision == self._new_revision_id and (path != '' or
234
253
                self._versioned_root)
235
254
 
 
255
        # XXX: Friction: parent_candidates should return a list not a dict
 
256
        #      so that we don't have to walk the inventories again.
236
257
        parent_candiate_entries = ie.parent_candidates(parent_invs)
237
 
        heads = self.repository.get_graph().heads(parent_candiate_entries.keys())
238
 
        # XXX: Note that this is unordered - and this is tolerable because 
239
 
        # the previous code was also unordered.
240
 
        previous_entries = dict((head, parent_candiate_entries[head]) for head
241
 
            in heads)
242
 
        # we are creating a new revision for ie in the history store and
243
 
        # inventory.
244
 
        ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
245
 
        return ie.revision == self._new_revision_id and (path != '' or
246
 
            self._versioned_root)
247
 
 
248
 
    def modified_directory(self, file_id, file_parents):
249
 
        """Record the presence of a symbolic link.
250
 
 
251
 
        :param file_id: The file_id of the link to record.
252
 
        :param file_parents: The per-file parent revision ids.
253
 
        """
254
 
        self._add_text_to_weave(file_id, [], file_parents.keys())
255
 
 
256
 
    def modified_reference(self, file_id, file_parents):
257
 
        """Record the modification of a reference.
258
 
 
259
 
        :param file_id: The file_id of the link to record.
260
 
        :param file_parents: The per-file parent revision ids.
261
 
        """
262
 
        self._add_text_to_weave(file_id, [], file_parents.keys())
263
 
    
264
 
    def modified_file_text(self, file_id, file_parents,
265
 
                           get_content_byte_lines, text_sha1=None,
266
 
                           text_size=None):
267
 
        """Record the text of file file_id
268
 
 
269
 
        :param file_id: The file_id of the file to record the text of.
270
 
        :param file_parents: The per-file parent revision ids.
271
 
        :param get_content_byte_lines: A callable which will return the byte
272
 
            lines for the file.
273
 
        :param text_sha1: Optional SHA1 of the file contents.
274
 
        :param text_size: Optional size of the file contents.
275
 
        """
276
 
        # mutter('storing text of file {%s} in revision {%s} into %r',
277
 
        #        file_id, self._new_revision_id, self.repository.weave_store)
278
 
        # special case to avoid diffing on renames or 
279
 
        # reparenting
280
 
        if (len(file_parents) == 1
281
 
            and text_sha1 == file_parents.values()[0].text_sha1
282
 
            and text_size == file_parents.values()[0].text_size):
283
 
            previous_ie = file_parents.values()[0]
284
 
            versionedfile = self.repository.weave_store.get_weave(file_id,
285
 
                self.repository.get_transaction())
286
 
            versionedfile.clone_text(self._new_revision_id,
287
 
                previous_ie.revision, file_parents.keys())
288
 
            return text_sha1, text_size
 
258
        head_set = self.repository.get_graph().heads(parent_candiate_entries.keys())
 
259
        heads = []
 
260
        for inv in parent_invs:
 
261
            if ie.file_id in inv:
 
262
                old_rev = inv[ie.file_id].revision
 
263
                if old_rev in head_set:
 
264
                    heads.append(inv[ie.file_id].revision)
 
265
                    head_set.remove(inv[ie.file_id].revision)
 
266
 
 
267
        store = False
 
268
        # now we check to see if we need to write a new record to the
 
269
        # file-graph.
 
270
        # We write a new entry unless there is one head to the ancestors, and
 
271
        # the kind-derived content is unchanged.
 
272
 
 
273
        # Cheapest check first: no ancestors, or more the one head in the
 
274
        # ancestors, we write a new node.
 
275
        if len(heads) != 1:
 
276
            store = True
 
277
        if not store:
 
278
            # There is a single head, look it up for comparison
 
279
            parent_entry = parent_candiate_entries[heads[0]]
 
280
            # if the non-content specific data has changed, we'll be writing a
 
281
            # node:
 
282
            if (parent_entry.parent_id != ie.parent_id or
 
283
                parent_entry.name != ie.name):
 
284
                store = True
 
285
        # now we need to do content specific checks:
 
286
        if not store:
 
287
            # if the kind changed the content obviously has
 
288
            if kind != parent_entry.kind:
 
289
                store = True
 
290
        if kind == 'file':
 
291
            if not store:
 
292
                if (# if the file length changed we have to store:
 
293
                    parent_entry.text_size != content_summary[1] or
 
294
                    # if the exec bit has changed we have to store:
 
295
                    parent_entry.executable != content_summary[2]):
 
296
                    store = True
 
297
                elif parent_entry.text_sha1 == content_summary[3]:
 
298
                    # all meta and content is unchanged (using a hash cache
 
299
                    # hit to check the sha)
 
300
                    ie.revision = parent_entry.revision
 
301
                    ie.text_size = parent_entry.text_size
 
302
                    ie.text_sha1 = parent_entry.text_sha1
 
303
                    ie.executable = parent_entry.executable
 
304
                    return
 
305
                else:
 
306
                    # Either there is only a hash change(no hash cache entry,
 
307
                    # or same size content change), or there is no change on
 
308
                    # this file at all.
 
309
                    # Provide the parent's hash to the store layer, so that the
 
310
                    # content is unchanged we will not store a new node.
 
311
                    nostore_sha = parent_entry.text_sha1
 
312
            if store:
 
313
                # We want to record a new node regardless of the presence or
 
314
                # absence of a content change in the file.
 
315
                nostore_sha = None
 
316
            ie.executable = content_summary[2]
 
317
            lines = tree.get_file(ie.file_id, path).readlines()
 
318
            try:
 
319
                ie.text_sha1, ie.text_size = self._add_text_to_weave(
 
320
                    ie.file_id, lines, heads, nostore_sha)
 
321
            except errors.ExistingContent:
 
322
                # Turns out that the file content was unchanged, and we were
 
323
                # only going to store a new node if it was changed. Carry over
 
324
                # the entry.
 
325
                ie.revision = parent_entry.revision
 
326
                ie.text_size = parent_entry.text_size
 
327
                ie.text_sha1 = parent_entry.text_sha1
 
328
                ie.executable = parent_entry.executable
 
329
                return
 
330
        elif kind == 'directory':
 
331
            if not store:
 
332
                # all data is meta here, nothing specific to directory, so
 
333
                # carry over:
 
334
                ie.revision = parent_entry.revision
 
335
                return
 
336
            lines = []
 
337
            self._add_text_to_weave(ie.file_id, lines, heads, None)
 
338
        elif kind == 'symlink':
 
339
            current_link_target = content_summary[3]
 
340
            if not store:
 
341
                # symlink target is not generic metadata, check if it has
 
342
                # changed.
 
343
                if current_link_target != parent_entry.symlink_target:
 
344
                    store = True
 
345
            if not store:
 
346
                # unchanged, carry over.
 
347
                ie.revision = parent_entry.revision
 
348
                ie.symlink_target = parent_entry.symlink_target
 
349
                return
 
350
            ie.symlink_target = current_link_target
 
351
            lines = []
 
352
            self._add_text_to_weave(ie.file_id, lines, heads, None)
 
353
        elif kind == 'tree-reference':
 
354
            if not store:
 
355
                if content_summary[3] != parent_entry.reference_revision:
 
356
                    store = True
 
357
            if not store:
 
358
                # unchanged, carry over.
 
359
                ie.reference_revision = parent_entry.reference_revision
 
360
                ie.revision = parent_entry.revision
 
361
                return
 
362
            ie.reference_revision = content_summary[3]
 
363
            lines = []
 
364
            self._add_text_to_weave(ie.file_id, lines, heads, None)
289
365
        else:
290
 
            new_lines = get_content_byte_lines()
291
 
            return self._add_text_to_weave(file_id, new_lines,
292
 
                file_parents.keys())
293
 
 
294
 
    def modified_link(self, file_id, file_parents, link_target):
295
 
        """Record the presence of a symbolic link.
296
 
 
297
 
        :param file_id: The file_id of the link to record.
298
 
        :param file_parents: The per-file parent revision ids.
299
 
        :param link_target: Target location of this link.
300
 
        """
301
 
        self._add_text_to_weave(file_id, [], file_parents.keys())
302
 
 
303
 
    def _add_text_to_weave(self, file_id, new_lines, parents):
 
366
            raise NotImplementedError('unknown kind')
 
367
        ie.revision = self._new_revision_id
 
368
        return True
 
369
 
 
370
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
304
371
        versionedfile = self.repository.weave_store.get_weave_or_empty(
305
372
            file_id, self.repository.get_transaction())
306
373
        # Don't change this to add_lines - add_lines_with_ghosts is cheaper
311
378
        # implementation could give us bad output from readlines() so this is
312
379
        # not a guarantee of safety. What would be better is always checking
313
380
        # the content during test suite execution. RBC 20070912
314
 
        result = versionedfile.add_lines_with_ghosts(
315
 
            self._new_revision_id, parents, new_lines,
316
 
            random_id=self.random_revid, check_content=False)[0:2]
317
 
        versionedfile.clear_cache()
318
 
        return result
 
381
        try:
 
382
            return versionedfile.add_lines_with_ghosts(
 
383
                self._new_revision_id, parents, new_lines,
 
384
                nostore_sha=nostore_sha, random_id=self.random_revid,
 
385
                check_content=False)[0:2]
 
386
        finally:
 
387
            versionedfile.clear_cache()
319
388
 
320
389
 
321
390
class RootCommitBuilder(CommitBuilder):