/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-09 20:23:07 UTC
  • mfrom: (4265.1.4 bbc-merge)
  • Revision ID: pqm@pqm.ubuntu.com-20090409202307-n0depb16qepoe21o
(jam) Change _fetch_uses_deltas = False for CHK repos until we can
        write a better fix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
49
49
# TODO: Change the parameter 'rev_id' to 'revision_id' to be consistent with
50
50
# the rest of the code; add a deprecation of the old name.
51
51
 
 
52
import os
 
53
import re
 
54
import sys
 
55
import time
 
56
 
 
57
from cStringIO import StringIO
 
58
 
52
59
from bzrlib import (
53
60
    debug,
54
61
    errors,
55
62
    revision,
56
63
    trace,
57
64
    tree,
58
 
    ui,
 
65
    xml_serializer,
59
66
    )
60
67
from bzrlib.branch import Branch
61
 
from bzrlib.cleanup import OperationWithCleanups
62
68
import bzrlib.config
63
69
from bzrlib.errors import (BzrError, PointlessCommit,
64
70
                           ConflictsInTree,
65
71
                           StrictCommitFailed
66
72
                           )
67
73
from bzrlib.osutils import (get_user_encoding,
68
 
                            is_inside_any,
 
74
                            kind_marker, isdir,isfile, is_inside_any,
 
75
                            is_inside_or_parent_of_any,
69
76
                            minimum_path_selection,
 
77
                            quotefn, sha_file, split_lines,
70
78
                            splitpath,
71
79
                            )
72
 
from bzrlib.trace import mutter, note, is_quiet
 
80
from bzrlib.testament import Testament
 
81
from bzrlib.trace import mutter, note, warning, is_quiet
73
82
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
74
83
from bzrlib import symbol_versioning
 
84
from bzrlib.symbol_versioning import (deprecated_passed,
 
85
        deprecated_function,
 
86
        DEPRECATED_PARAMETER)
 
87
from bzrlib.workingtree import WorkingTree
75
88
from bzrlib.urlutils import unescape_for_display
 
89
import bzrlib.ui
76
90
 
77
91
 
78
92
class NullCommitReporter(object):
94
108
    def deleted(self, path):
95
109
        pass
96
110
 
 
111
    def escaped(self, escape_count, message):
 
112
        pass
 
113
 
97
114
    def missing(self, path):
98
115
        pass
99
116
 
132
149
 
133
150
    def completed(self, revno, rev_id):
134
151
        self._note('Committed revision %d.', revno)
135
 
        # self._note goes to the console too; so while we want to log the
136
 
        # rev_id, we can't trivially only log it. (See bug 526425). Long
137
 
        # term we should rearrange the reporting structure, but for now
138
 
        # we just mutter seperately. We mutter the revid and revno together
139
 
        # so that concurrent bzr invocations won't lead to confusion.
140
 
        mutter('Committed revid %s as revno %d.', rev_id, revno)
141
152
 
142
153
    def deleted(self, path):
143
154
        self._note('deleted %s', path)
144
155
 
 
156
    def escaped(self, escape_count, message):
 
157
        self._note("replaced %d control characters in message", escape_count)
 
158
 
145
159
    def missing(self, path):
146
160
        self._note('missing %s', path)
147
161
 
174
188
        self.reporter = reporter
175
189
        self.config = config
176
190
 
177
 
    @staticmethod
178
 
    def update_revprops(revprops, branch, authors=None, author=None,
179
 
                        local=False, possible_master_transports=None):
180
 
        if revprops is None:
181
 
            revprops = {}
182
 
        if possible_master_transports is None:
183
 
            possible_master_transports = []
184
 
        if not 'branch-nick' in revprops:
185
 
            revprops['branch-nick'] = branch._get_nick(
186
 
                local,
187
 
                possible_master_transports)
188
 
        if authors is not None:
189
 
            if author is not None:
190
 
                raise AssertionError('Specifying both author and authors '
191
 
                        'is not allowed. Specify just authors instead')
192
 
            if 'author' in revprops or 'authors' in revprops:
193
 
                # XXX: maybe we should just accept one of them?
194
 
                raise AssertionError('author property given twice')
195
 
            if authors:
196
 
                for individual in authors:
197
 
                    if '\n' in individual:
198
 
                        raise AssertionError('\\n is not a valid character '
199
 
                                'in an author identity')
200
 
                revprops['authors'] = '\n'.join(authors)
201
 
        if author is not None:
202
 
            symbol_versioning.warn('The parameter author was deprecated'
203
 
                   ' in version 1.13. Use authors instead',
204
 
                   DeprecationWarning)
205
 
            if 'author' in revprops or 'authors' in revprops:
206
 
                # XXX: maybe we should just accept one of them?
207
 
                raise AssertionError('author property given twice')
208
 
            if '\n' in author:
209
 
                raise AssertionError('\\n is not a valid character '
210
 
                        'in an author identity')
211
 
            revprops['authors'] = author
212
 
        return revprops
213
 
 
214
191
    def commit(self,
215
192
               message=None,
216
193
               timestamp=None,
233
210
        """Commit working copy as a new revision.
234
211
 
235
212
        :param message: the commit message (it or message_callback is required)
236
 
        :param message_callback: A callback: message = message_callback(cmt_obj)
237
213
 
238
214
        :param timestamp: if not None, seconds-since-epoch for a
239
215
            postdated/predated commit.
240
216
 
241
 
        :param specific_files: If not None, commit only those files. An empty
242
 
            list means 'commit no files'.
 
217
        :param specific_files: If true, commit only those files.
243
218
 
244
219
        :param rev_id: If set, use this as the new revision id.
245
220
            Useful for test or import commands that need to tightly
263
238
            commit. Pending changes to excluded files will be ignored by the
264
239
            commit.
265
240
        """
266
 
        operation = OperationWithCleanups(self._commit)
267
 
        self.revprops = revprops or {}
268
 
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
269
 
        self.config = config or self.config
270
 
        return operation.run(
271
 
               message=message,
272
 
               timestamp=timestamp,
273
 
               timezone=timezone,
274
 
               committer=committer,
275
 
               specific_files=specific_files,
276
 
               rev_id=rev_id,
277
 
               allow_pointless=allow_pointless,
278
 
               strict=strict,
279
 
               verbose=verbose,
280
 
               working_tree=working_tree,
281
 
               local=local,
282
 
               reporter=reporter,
283
 
               message_callback=message_callback,
284
 
               recursive=recursive,
285
 
               exclude=exclude,
286
 
               possible_master_transports=possible_master_transports)
287
 
 
288
 
    def _commit(self, operation, message, timestamp, timezone, committer,
289
 
            specific_files, rev_id, allow_pointless, strict, verbose,
290
 
            working_tree, local, reporter, message_callback, recursive,
291
 
            exclude, possible_master_transports):
292
241
        mutter('preparing to commit')
293
242
 
294
243
        if working_tree is None:
317
266
            self.exclude = []
318
267
        self.local = local
319
268
        self.master_branch = None
 
269
        self.master_locked = False
320
270
        self.recursive = recursive
321
271
        self.rev_id = None
322
 
        # self.specific_files is None to indicate no filter, or any iterable to
323
 
        # indicate a filter - [] means no files at all, as per iter_changes.
324
272
        if specific_files is not None:
325
273
            self.specific_files = sorted(
326
274
                minimum_path_selection(specific_files))
328
276
            self.specific_files = None
329
277
            
330
278
        self.allow_pointless = allow_pointless
 
279
        self.revprops = revprops
331
280
        self.message_callback = message_callback
332
281
        self.timestamp = timestamp
333
282
        self.timezone = timezone
336
285
        self.verbose = verbose
337
286
 
338
287
        self.work_tree.lock_write()
339
 
        operation.add_cleanup(self.work_tree.unlock)
340
288
        self.parents = self.work_tree.get_parent_ids()
341
289
        # We can use record_iter_changes IFF iter_changes is compatible with
342
290
        # the command line parameters, and the repository has fast delta
343
291
        # generation. See bug 347649.
344
292
        self.use_record_iter_changes = (
 
293
            not self.specific_files and
345
294
            not self.exclude and 
346
295
            not self.branch.repository._format.supports_tree_reference and
347
296
            (self.branch.repository._format.fast_deltas or
348
297
             len(self.parents) < 2))
349
 
        self.pb = ui.ui_factory.nested_progress_bar()
350
 
        operation.add_cleanup(self.pb.finished)
 
298
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
351
299
        self.basis_revid = self.work_tree.last_revision()
352
300
        self.basis_tree = self.work_tree.basis_tree()
353
301
        self.basis_tree.lock_read()
354
 
        operation.add_cleanup(self.basis_tree.unlock)
355
 
        # Cannot commit with conflicts present.
356
 
        if len(self.work_tree.conflicts()) > 0:
357
 
            raise ConflictsInTree
358
 
 
359
 
        # Setup the bound branch variables as needed.
360
 
        self._check_bound_branch(operation, possible_master_transports)
361
 
 
362
 
        # Check that the working tree is up to date
363
 
        old_revno, new_revno = self._check_out_of_date_tree()
364
 
 
365
 
        # Complete configuration setup
366
 
        if reporter is not None:
367
 
            self.reporter = reporter
368
 
        elif self.reporter is None:
369
 
            self.reporter = self._select_reporter()
370
 
        if self.config is None:
371
 
            self.config = self.branch.get_config()
372
 
 
373
 
        self._set_specific_file_ids()
374
 
 
375
 
        # Setup the progress bar. As the number of files that need to be
376
 
        # committed in unknown, progress is reported as stages.
377
 
        # We keep track of entries separately though and include that
378
 
        # information in the progress bar during the relevant stages.
379
 
        self.pb_stage_name = ""
380
 
        self.pb_stage_count = 0
381
 
        self.pb_stage_total = 5
382
 
        if self.bound_branch:
383
 
            # 2 extra stages: "Uploading data to master branch" and "Merging
384
 
            # tags to master branch"
385
 
            self.pb_stage_total += 2
386
 
        self.pb.show_pct = False
387
 
        self.pb.show_spinner = False
388
 
        self.pb.show_eta = False
389
 
        self.pb.show_count = True
390
 
        self.pb.show_bar = True
391
 
 
392
 
        self._gather_parents()
393
 
        # After a merge, a selected file commit is not supported.
394
 
        # See 'bzr help merge' for an explanation as to why.
395
 
        if len(self.parents) > 1 and self.specific_files is not None:
396
 
            raise errors.CannotCommitSelectedFileMerge(self.specific_files)
397
 
        # Excludes are a form of selected file commit.
398
 
        if len(self.parents) > 1 and self.exclude:
399
 
            raise errors.CannotCommitSelectedFileMerge(self.exclude)
400
 
 
401
 
        # Collect the changes
402
 
        self._set_progress_stage("Collecting changes", counter=True)
403
 
        self.builder = self.branch.get_commit_builder(self.parents,
404
 
            self.config, timestamp, timezone, committer, self.revprops, rev_id)
405
 
 
406
302
        try:
407
 
            self.builder.will_record_deletes()
408
 
            # find the location being committed to
409
 
            if self.bound_branch:
410
 
                master_location = self.master_branch.base
411
 
            else:
412
 
                master_location = self.branch.base
413
 
 
414
 
            # report the start of the commit
415
 
            self.reporter.started(new_revno, self.rev_id, master_location)
416
 
 
417
 
            self._update_builder_with_changes()
418
 
            self._check_pointless()
419
 
 
420
 
            # TODO: Now the new inventory is known, check for conflicts.
421
 
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
422
 
            # weave lines, because nothing should be recorded until it is known
423
 
            # that commit will succeed.
424
 
            self._set_progress_stage("Saving data locally")
425
 
            self.builder.finish_inventory()
426
 
 
427
 
            # Prompt the user for a commit message if none provided
428
 
            message = message_callback(self)
429
 
            self.message = message
430
 
 
431
 
            # Add revision data to the local branch
432
 
            self.rev_id = self.builder.commit(self.message)
433
 
 
434
 
        except Exception, e:
435
 
            mutter("aborting commit write group because of exception:")
436
 
            trace.log_exception_quietly()
437
 
            note("aborting commit write group: %r" % (e,))
438
 
            self.builder.abort()
439
 
            raise
440
 
 
441
 
        self._process_pre_hooks(old_revno, new_revno)
442
 
 
443
 
        # Upload revision data to the master.
444
 
        # this will propagate merged revisions too if needed.
445
 
        if self.bound_branch:
446
 
            self._set_progress_stage("Uploading data to master branch")
447
 
            # 'commit' to the master first so a timeout here causes the
448
 
            # local branch to be out of date
449
 
            self.master_branch.import_last_revision_info_and_tags(
450
 
                self.branch, new_revno, self.rev_id)
451
 
 
452
 
        # and now do the commit locally.
453
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
454
 
 
455
 
        # Merge local tags to remote
456
 
        if self.bound_branch:
457
 
            self._set_progress_stage("Merging tags to master branch")
458
 
            tag_conflicts = self.branch.tags.merge_to(self.master_branch.tags)
459
 
            if tag_conflicts:
460
 
                warning_lines = ['    ' + name for name, _, _ in tag_conflicts]
461
 
                note("Conflicting tags in bound branch:\n" +
462
 
                    "\n".join(warning_lines))
463
 
 
464
 
        # Make the working tree be up to date with the branch. This
465
 
        # includes automatic changes scheduled to be made to the tree, such
466
 
        # as updating its basis and unversioning paths that were missing.
467
 
        self.work_tree.unversion(self.deleted_ids)
468
 
        self._set_progress_stage("Updating the working tree")
469
 
        self.work_tree.update_basis_by_delta(self.rev_id,
470
 
             self.builder.get_basis_delta())
471
 
        self.reporter.completed(new_revno, self.rev_id)
472
 
        self._process_post_hooks(old_revno, new_revno)
 
303
            # Cannot commit with conflicts present.
 
304
            if len(self.work_tree.conflicts()) > 0:
 
305
                raise ConflictsInTree
 
306
 
 
307
            # Setup the bound branch variables as needed.
 
308
            self._check_bound_branch(possible_master_transports)
 
309
 
 
310
            # Check that the working tree is up to date
 
311
            old_revno, new_revno = self._check_out_of_date_tree()
 
312
 
 
313
            # Complete configuration setup
 
314
            if reporter is not None:
 
315
                self.reporter = reporter
 
316
            elif self.reporter is None:
 
317
                self.reporter = self._select_reporter()
 
318
            if self.config is None:
 
319
                self.config = self.branch.get_config()
 
320
 
 
321
            self._set_specific_file_ids()
 
322
 
 
323
            # Setup the progress bar. As the number of files that need to be
 
324
            # committed in unknown, progress is reported as stages.
 
325
            # We keep track of entries separately though and include that
 
326
            # information in the progress bar during the relevant stages.
 
327
            self.pb_stage_name = ""
 
328
            self.pb_stage_count = 0
 
329
            self.pb_stage_total = 5
 
330
            if self.bound_branch:
 
331
                self.pb_stage_total += 1
 
332
            self.pb.show_pct = False
 
333
            self.pb.show_spinner = False
 
334
            self.pb.show_eta = False
 
335
            self.pb.show_count = True
 
336
            self.pb.show_bar = True
 
337
 
 
338
            self._gather_parents()
 
339
            # After a merge, a selected file commit is not supported.
 
340
            # See 'bzr help merge' for an explanation as to why.
 
341
            if len(self.parents) > 1 and self.specific_files:
 
342
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
 
343
            # Excludes are a form of selected file commit.
 
344
            if len(self.parents) > 1 and self.exclude:
 
345
                raise errors.CannotCommitSelectedFileMerge(self.exclude)
 
346
 
 
347
            # Collect the changes
 
348
            self._set_progress_stage("Collecting changes", counter=True)
 
349
            self.builder = self.branch.get_commit_builder(self.parents,
 
350
                self.config, timestamp, timezone, committer, revprops, rev_id)
 
351
 
 
352
            try:
 
353
                self.builder.will_record_deletes()
 
354
                # find the location being committed to
 
355
                if self.bound_branch:
 
356
                    master_location = self.master_branch.base
 
357
                else:
 
358
                    master_location = self.branch.base
 
359
 
 
360
                # report the start of the commit
 
361
                self.reporter.started(new_revno, self.rev_id, master_location)
 
362
 
 
363
                self._update_builder_with_changes()
 
364
                self._check_pointless()
 
365
 
 
366
                # TODO: Now the new inventory is known, check for conflicts.
 
367
                # ADHB 2006-08-08: If this is done, populate_new_inv should not add
 
368
                # weave lines, because nothing should be recorded until it is known
 
369
                # that commit will succeed.
 
370
                self._set_progress_stage("Saving data locally")
 
371
                self.builder.finish_inventory()
 
372
 
 
373
                # Prompt the user for a commit message if none provided
 
374
                message = message_callback(self)
 
375
                self.message = message
 
376
                self._escape_commit_message()
 
377
 
 
378
                # Add revision data to the local branch
 
379
                self.rev_id = self.builder.commit(self.message)
 
380
 
 
381
            except Exception, e:
 
382
                mutter("aborting commit write group because of exception:")
 
383
                trace.log_exception_quietly()
 
384
                note("aborting commit write group: %r" % (e,))
 
385
                self.builder.abort()
 
386
                raise
 
387
 
 
388
            self._process_pre_hooks(old_revno, new_revno)
 
389
 
 
390
            # Upload revision data to the master.
 
391
            # this will propagate merged revisions too if needed.
 
392
            if self.bound_branch:
 
393
                self._set_progress_stage("Uploading data to master branch")
 
394
                # 'commit' to the master first so a timeout here causes the
 
395
                # local branch to be out of date
 
396
                self.master_branch.import_last_revision_info(
 
397
                    self.branch.repository, new_revno, self.rev_id)
 
398
 
 
399
            # and now do the commit locally.
 
400
            self.branch.set_last_revision_info(new_revno, self.rev_id)
 
401
 
 
402
            # Make the working tree up to date with the branch
 
403
            self._set_progress_stage("Updating the working tree")
 
404
            self.work_tree.update_basis_by_delta(self.rev_id,
 
405
                 self.builder.get_basis_delta())
 
406
            self.reporter.completed(new_revno, self.rev_id)
 
407
            self._process_post_hooks(old_revno, new_revno)
 
408
        finally:
 
409
            self._cleanup()
473
410
        return self.rev_id
474
411
 
475
412
    def _select_reporter(self):
497
434
            return
498
435
        raise PointlessCommit()
499
436
 
500
 
    def _check_bound_branch(self, operation, possible_master_transports=None):
 
437
    def _check_bound_branch(self, possible_master_transports=None):
501
438
        """Check to see if the local branch is bound.
502
439
 
503
440
        If it is bound, then most of the commit will actually be
538
475
        # so grab the lock
539
476
        self.bound_branch = self.branch
540
477
        self.master_branch.lock_write()
541
 
        operation.add_cleanup(self.master_branch.unlock)
 
478
        self.master_locked = True
542
479
 
543
480
    def _check_out_of_date_tree(self):
544
481
        """Check that the working tree is up to date.
629
566
                     old_revno, old_revid, new_revno, self.rev_id,
630
567
                     tree_delta, future_tree)
631
568
 
 
569
    def _cleanup(self):
 
570
        """Cleanup any open locks, progress bars etc."""
 
571
        cleanups = [self._cleanup_bound_branch,
 
572
                    self.basis_tree.unlock,
 
573
                    self.work_tree.unlock,
 
574
                    self.pb.finished]
 
575
        found_exception = None
 
576
        for cleanup in cleanups:
 
577
            try:
 
578
                cleanup()
 
579
            # we want every cleanup to run no matter what.
 
580
            # so we have a catchall here, but we will raise the
 
581
            # last encountered exception up the stack: and
 
582
            # typically this will be useful enough.
 
583
            except Exception, e:
 
584
                found_exception = e
 
585
        if found_exception is not None:
 
586
            # don't do a plan raise, because the last exception may have been
 
587
            # trashed, e is our sure-to-work exception even though it loses the
 
588
            # full traceback. XXX: RBC 20060421 perhaps we could check the
 
589
            # exc_info and if its the same one do a plain raise otherwise
 
590
            # 'raise e' as we do now.
 
591
            raise e
 
592
 
 
593
    def _cleanup_bound_branch(self):
 
594
        """Executed at the end of a try/finally to cleanup a bound branch.
 
595
 
 
596
        If the branch wasn't bound, this is a no-op.
 
597
        If it was, it resents self.branch to the local branch, instead
 
598
        of being the master.
 
599
        """
 
600
        if not self.bound_branch:
 
601
            return
 
602
        if self.master_locked:
 
603
            self.master_branch.unlock()
 
604
 
 
605
    def _escape_commit_message(self):
 
606
        """Replace xml-incompatible control characters."""
 
607
        # FIXME: RBC 20060419 this should be done by the revision
 
608
        # serialiser not by commit. Then we can also add an unescaper
 
609
        # in the deserializer and start roundtripping revision messages
 
610
        # precisely. See repository_implementations/test_repository.py
 
611
        self.message, escape_count = xml_serializer.escape_invalid_chars(
 
612
            self.message)
 
613
        if escape_count:
 
614
            self.reporter.escaped(escape_count, self.message)
 
615
 
632
616
    def _gather_parents(self):
633
617
        """Record the parents of a merge for merge detection."""
634
618
        # TODO: Make sure that this list doesn't contain duplicate
649
633
        """Update the commit builder with the data about what has changed.
650
634
        """
651
635
        exclude = self.exclude
652
 
        specific_files = self.specific_files
 
636
        specific_files = self.specific_files or []
653
637
        mutter("Selecting files for commit with filter %s", specific_files)
654
638
 
655
639
        self._check_strict()
656
640
        if self.use_record_iter_changes:
657
 
            iter_changes = self.work_tree.iter_changes(self.basis_tree,
658
 
                specific_files=specific_files)
 
641
            iter_changes = self.work_tree.iter_changes(self.basis_tree)
659
642
            iter_changes = self._filter_iter_changes(iter_changes)
660
643
            for file_id, path, fs_hash in self.builder.record_iter_changes(
661
644
                self.work_tree, self.basis_revid, iter_changes):
714
697
                            reporter.snapshot_change('modified', new_path)
715
698
            self._next_progress_entry()
716
699
        # Unversion IDs that were found to be deleted
717
 
        self.deleted_ids = deleted_ids
 
700
        self.work_tree.unversion(deleted_ids)
718
701
 
719
702
    def _record_unselected(self):
720
703
        # If specific files are selected, then all un-selected files must be
833
816
                # _update_builder_with_changes.
834
817
                continue
835
818
            content_summary = self.work_tree.path_content_summary(path)
836
 
            kind = content_summary[0]
837
819
            # Note that when a filter of specific files is given, we must only
838
820
            # skip/record deleted files matching that filter.
839
821
            if not specific_files or is_inside_any(specific_files, path):
840
 
                if kind == 'missing':
 
822
                if content_summary[0] == 'missing':
841
823
                    if not deleted_paths:
842
824
                        # path won't have been split yet.
843
825
                        path_segments = splitpath(path)
850
832
                    continue
851
833
            # TODO: have the builder do the nested commit just-in-time IF and
852
834
            # only if needed.
853
 
            if kind == 'tree-reference':
 
835
            if content_summary[0] == 'tree-reference':
854
836
                # enforce repository nested tree policy.
855
837
                if (not self.work_tree.supports_tree_reference() or
856
838
                    # repository does not support it either.
857
839
                    not self.branch.repository._format.supports_tree_reference):
858
 
                    kind = 'directory'
859
 
                    content_summary = (kind, None, None, None)
860
 
                elif self.recursive == 'down':
 
840
                    content_summary = ('directory',) + content_summary[1:]
 
841
            kind = content_summary[0]
 
842
            # TODO: specific_files filtering before nested tree processing
 
843
            if kind == 'tree-reference':
 
844
                if self.recursive == 'down':
861
845
                    nested_revision_id = self._commit_nested_tree(
862
846
                        file_id, path)
863
 
                    content_summary = (kind, None, None, nested_revision_id)
 
847
                    content_summary = content_summary[:3] + (
 
848
                        nested_revision_id,)
864
849
                else:
865
 
                    nested_revision_id = self.work_tree.get_reference_revision(file_id)
866
 
                    content_summary = (kind, None, None, nested_revision_id)
 
850
                    content_summary = content_summary[:3] + (
 
851
                        self.work_tree.get_reference_revision(file_id),)
867
852
 
868
853
            # Record an entry for this item
869
854
            # Note: I don't particularly want to have the existing_ie
875
860
                content_summary)
876
861
 
877
862
        # Unversion IDs that were found to be deleted
878
 
        self.deleted_ids = deleted_ids
 
863
        self.work_tree.unversion(deleted_ids)
879
864
 
880
865
    def _commit_nested_tree(self, file_id, path):
881
866
        "Commit a nested tree."