/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: John Arbash Meinel
  • Date: 2009-08-17 15:38:52 UTC
  • mfrom: (4615.1.1 2.0b1-merge-sort)
  • mto: This revision was merged to the branch mainline in revision 4629.
  • Revision ID: john@arbash-meinel.com-20090817153852-9127xf3aabmcoegt
Bring in the changes to merge_sort.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 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
65
65
    xml_serializer,
66
66
    )
67
67
from bzrlib.branch import Branch
68
 
from bzrlib.cleanup import OperationWithCleanups
69
68
import bzrlib.config
70
69
from bzrlib.errors import (BzrError, PointlessCommit,
71
70
                           ConflictsInTree,
147
146
 
148
147
    def completed(self, revno, rev_id):
149
148
        self._note('Committed revision %d.', revno)
150
 
        # self._note goes to the console too; so while we want to log the
151
 
        # rev_id, we can't trivially only log it. (See bug 526425). Long
152
 
        # term we should rearrange the reporting structure, but for now
153
 
        # we just mutter seperately. We mutter the revid and revno together
154
 
        # so that concurrent bzr invocations won't lead to confusion.
155
 
        mutter('Committed revid %s as revno %d.', rev_id, revno)
156
149
 
157
150
    def deleted(self, path):
158
151
        self._note('deleted %s', path)
216
209
        :param timestamp: if not None, seconds-since-epoch for a
217
210
            postdated/predated commit.
218
211
 
219
 
        :param specific_files: If not None, commit only those files. An empty
220
 
            list means 'commit no files'.
 
212
        :param specific_files: If true, commit only those files.
221
213
 
222
214
        :param rev_id: If set, use this as the new revision id.
223
215
            Useful for test or import commands that need to tightly
241
233
            commit. Pending changes to excluded files will be ignored by the
242
234
            commit.
243
235
        """
244
 
        operation = OperationWithCleanups(self._commit)
245
 
        self.revprops = revprops or {}
246
 
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
247
 
        self.config = config or self.config
248
 
        return operation.run(
249
 
               message=message,
250
 
               timestamp=timestamp,
251
 
               timezone=timezone,
252
 
               committer=committer,
253
 
               specific_files=specific_files,
254
 
               rev_id=rev_id,
255
 
               allow_pointless=allow_pointless,
256
 
               strict=strict,
257
 
               verbose=verbose,
258
 
               working_tree=working_tree,
259
 
               local=local,
260
 
               reporter=reporter,
261
 
               message_callback=message_callback,
262
 
               recursive=recursive,
263
 
               exclude=exclude,
264
 
               possible_master_transports=possible_master_transports)
265
 
 
266
 
    def _commit(self, operation, message, timestamp, timezone, committer,
267
 
            specific_files, rev_id, allow_pointless, strict, verbose,
268
 
            working_tree, local, reporter, message_callback, recursive,
269
 
            exclude, possible_master_transports):
270
236
        mutter('preparing to commit')
271
237
 
272
238
        if working_tree is None:
295
261
            self.exclude = []
296
262
        self.local = local
297
263
        self.master_branch = None
 
264
        self.master_locked = False
298
265
        self.recursive = recursive
299
266
        self.rev_id = None
300
 
        # self.specific_files is None to indicate no filter, or any iterable to
301
 
        # indicate a filter - [] means no files at all, as per iter_changes.
302
267
        if specific_files is not None:
303
268
            self.specific_files = sorted(
304
269
                minimum_path_selection(specific_files))
306
271
            self.specific_files = None
307
272
            
308
273
        self.allow_pointless = allow_pointless
 
274
        self.revprops = revprops
309
275
        self.message_callback = message_callback
310
276
        self.timestamp = timestamp
311
277
        self.timezone = timezone
314
280
        self.verbose = verbose
315
281
 
316
282
        self.work_tree.lock_write()
317
 
        operation.add_cleanup(self.work_tree.unlock)
318
283
        self.parents = self.work_tree.get_parent_ids()
319
284
        # We can use record_iter_changes IFF iter_changes is compatible with
320
285
        # the command line parameters, and the repository has fast delta
321
286
        # generation. See bug 347649.
322
287
        self.use_record_iter_changes = (
 
288
            not self.specific_files and
323
289
            not self.exclude and 
324
290
            not self.branch.repository._format.supports_tree_reference and
325
291
            (self.branch.repository._format.fast_deltas or
326
292
             len(self.parents) < 2))
327
293
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
328
 
        operation.add_cleanup(self.pb.finished)
329
294
        self.basis_revid = self.work_tree.last_revision()
330
295
        self.basis_tree = self.work_tree.basis_tree()
331
296
        self.basis_tree.lock_read()
332
 
        operation.add_cleanup(self.basis_tree.unlock)
333
 
        # Cannot commit with conflicts present.
334
 
        if len(self.work_tree.conflicts()) > 0:
335
 
            raise ConflictsInTree
336
 
 
337
 
        # Setup the bound branch variables as needed.
338
 
        self._check_bound_branch(operation, possible_master_transports)
339
 
 
340
 
        # Check that the working tree is up to date
341
 
        old_revno, new_revno = self._check_out_of_date_tree()
342
 
 
343
 
        # Complete configuration setup
344
 
        if reporter is not None:
345
 
            self.reporter = reporter
346
 
        elif self.reporter is None:
347
 
            self.reporter = self._select_reporter()
348
 
        if self.config is None:
349
 
            self.config = self.branch.get_config()
350
 
 
351
 
        self._set_specific_file_ids()
352
 
 
353
 
        # Setup the progress bar. As the number of files that need to be
354
 
        # committed in unknown, progress is reported as stages.
355
 
        # We keep track of entries separately though and include that
356
 
        # information in the progress bar during the relevant stages.
357
 
        self.pb_stage_name = ""
358
 
        self.pb_stage_count = 0
359
 
        self.pb_stage_total = 5
360
 
        if self.bound_branch:
361
 
            self.pb_stage_total += 1
362
 
        self.pb.show_pct = False
363
 
        self.pb.show_spinner = False
364
 
        self.pb.show_eta = False
365
 
        self.pb.show_count = True
366
 
        self.pb.show_bar = True
367
 
 
368
 
        self._gather_parents()
369
 
        # After a merge, a selected file commit is not supported.
370
 
        # See 'bzr help merge' for an explanation as to why.
371
 
        if len(self.parents) > 1 and self.specific_files is not None:
372
 
            raise errors.CannotCommitSelectedFileMerge(self.specific_files)
373
 
        # Excludes are a form of selected file commit.
374
 
        if len(self.parents) > 1 and self.exclude:
375
 
            raise errors.CannotCommitSelectedFileMerge(self.exclude)
376
 
 
377
 
        # Collect the changes
378
 
        self._set_progress_stage("Collecting changes", counter=True)
379
 
        self.builder = self.branch.get_commit_builder(self.parents,
380
 
            self.config, timestamp, timezone, committer, self.revprops, rev_id)
381
 
 
382
297
        try:
383
 
            self.builder.will_record_deletes()
384
 
            # find the location being committed to
385
 
            if self.bound_branch:
386
 
                master_location = self.master_branch.base
387
 
            else:
388
 
                master_location = self.branch.base
389
 
 
390
 
            # report the start of the commit
391
 
            self.reporter.started(new_revno, self.rev_id, master_location)
392
 
 
393
 
            self._update_builder_with_changes()
394
 
            self._check_pointless()
395
 
 
396
 
            # TODO: Now the new inventory is known, check for conflicts.
397
 
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
398
 
            # weave lines, because nothing should be recorded until it is known
399
 
            # that commit will succeed.
400
 
            self._set_progress_stage("Saving data locally")
401
 
            self.builder.finish_inventory()
402
 
 
403
 
            # Prompt the user for a commit message if none provided
404
 
            message = message_callback(self)
405
 
            self.message = message
406
 
 
407
 
            # Add revision data to the local branch
408
 
            self.rev_id = self.builder.commit(self.message)
409
 
 
410
 
        except Exception, e:
411
 
            mutter("aborting commit write group because of exception:")
412
 
            trace.log_exception_quietly()
413
 
            note("aborting commit write group: %r" % (e,))
414
 
            self.builder.abort()
415
 
            raise
416
 
 
417
 
        self._process_pre_hooks(old_revno, new_revno)
418
 
 
419
 
        # Upload revision data to the master.
420
 
        # this will propagate merged revisions too if needed.
421
 
        if self.bound_branch:
422
 
            self._set_progress_stage("Uploading data to master branch")
423
 
            # 'commit' to the master first so a timeout here causes the
424
 
            # local branch to be out of date
425
 
            self.master_branch.import_last_revision_info(
426
 
                self.branch.repository, new_revno, self.rev_id)
427
 
 
428
 
        # and now do the commit locally.
429
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
430
 
 
431
 
        # Make the working tree be up to date with the branch. This
432
 
        # includes automatic changes scheduled to be made to the tree, such
433
 
        # as updating its basis and unversioning paths that were missing.
434
 
        self.work_tree.unversion(self.deleted_ids)
435
 
        self._set_progress_stage("Updating the working tree")
436
 
        self.work_tree.update_basis_by_delta(self.rev_id,
437
 
             self.builder.get_basis_delta())
438
 
        self.reporter.completed(new_revno, self.rev_id)
439
 
        self._process_post_hooks(old_revno, new_revno)
 
298
            # Cannot commit with conflicts present.
 
299
            if len(self.work_tree.conflicts()) > 0:
 
300
                raise ConflictsInTree
 
301
 
 
302
            # Setup the bound branch variables as needed.
 
303
            self._check_bound_branch(possible_master_transports)
 
304
 
 
305
            # Check that the working tree is up to date
 
306
            old_revno, new_revno = self._check_out_of_date_tree()
 
307
 
 
308
            # Complete configuration setup
 
309
            if reporter is not None:
 
310
                self.reporter = reporter
 
311
            elif self.reporter is None:
 
312
                self.reporter = self._select_reporter()
 
313
            if self.config is None:
 
314
                self.config = self.branch.get_config()
 
315
 
 
316
            self._set_specific_file_ids()
 
317
 
 
318
            # Setup the progress bar. As the number of files that need to be
 
319
            # committed in unknown, progress is reported as stages.
 
320
            # We keep track of entries separately though and include that
 
321
            # information in the progress bar during the relevant stages.
 
322
            self.pb_stage_name = ""
 
323
            self.pb_stage_count = 0
 
324
            self.pb_stage_total = 5
 
325
            if self.bound_branch:
 
326
                self.pb_stage_total += 1
 
327
            self.pb.show_pct = False
 
328
            self.pb.show_spinner = False
 
329
            self.pb.show_eta = False
 
330
            self.pb.show_count = True
 
331
            self.pb.show_bar = True
 
332
 
 
333
            self._gather_parents()
 
334
            # After a merge, a selected file commit is not supported.
 
335
            # See 'bzr help merge' for an explanation as to why.
 
336
            if len(self.parents) > 1 and self.specific_files:
 
337
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
 
338
            # Excludes are a form of selected file commit.
 
339
            if len(self.parents) > 1 and self.exclude:
 
340
                raise errors.CannotCommitSelectedFileMerge(self.exclude)
 
341
 
 
342
            # Collect the changes
 
343
            self._set_progress_stage("Collecting changes", counter=True)
 
344
            self.builder = self.branch.get_commit_builder(self.parents,
 
345
                self.config, timestamp, timezone, committer, revprops, rev_id)
 
346
 
 
347
            try:
 
348
                self.builder.will_record_deletes()
 
349
                # find the location being committed to
 
350
                if self.bound_branch:
 
351
                    master_location = self.master_branch.base
 
352
                else:
 
353
                    master_location = self.branch.base
 
354
 
 
355
                # report the start of the commit
 
356
                self.reporter.started(new_revno, self.rev_id, master_location)
 
357
 
 
358
                self._update_builder_with_changes()
 
359
                self._check_pointless()
 
360
 
 
361
                # TODO: Now the new inventory is known, check for conflicts.
 
362
                # ADHB 2006-08-08: If this is done, populate_new_inv should not add
 
363
                # weave lines, because nothing should be recorded until it is known
 
364
                # that commit will succeed.
 
365
                self._set_progress_stage("Saving data locally")
 
366
                self.builder.finish_inventory()
 
367
 
 
368
                # Prompt the user for a commit message if none provided
 
369
                message = message_callback(self)
 
370
                self.message = message
 
371
 
 
372
                # Add revision data to the local branch
 
373
                self.rev_id = self.builder.commit(self.message)
 
374
 
 
375
            except Exception, e:
 
376
                mutter("aborting commit write group because of exception:")
 
377
                trace.log_exception_quietly()
 
378
                note("aborting commit write group: %r" % (e,))
 
379
                self.builder.abort()
 
380
                raise
 
381
 
 
382
            self._process_pre_hooks(old_revno, new_revno)
 
383
 
 
384
            # Upload revision data to the master.
 
385
            # this will propagate merged revisions too if needed.
 
386
            if self.bound_branch:
 
387
                self._set_progress_stage("Uploading data to master branch")
 
388
                # 'commit' to the master first so a timeout here causes the
 
389
                # local branch to be out of date
 
390
                self.master_branch.import_last_revision_info(
 
391
                    self.branch.repository, new_revno, self.rev_id)
 
392
 
 
393
            # and now do the commit locally.
 
394
            self.branch.set_last_revision_info(new_revno, self.rev_id)
 
395
 
 
396
            # Make the working tree be up to date with the branch. This
 
397
            # includes automatic changes scheduled to be made to the tree, such
 
398
            # as updating its basis and unversioning paths that were missing.
 
399
            self.work_tree.unversion(self.deleted_ids)
 
400
            self._set_progress_stage("Updating the working tree")
 
401
            self.work_tree.update_basis_by_delta(self.rev_id,
 
402
                 self.builder.get_basis_delta())
 
403
            self.reporter.completed(new_revno, self.rev_id)
 
404
            self._process_post_hooks(old_revno, new_revno)
 
405
        finally:
 
406
            self._cleanup()
440
407
        return self.rev_id
441
408
 
442
409
    def _select_reporter(self):
464
431
            return
465
432
        raise PointlessCommit()
466
433
 
467
 
    def _check_bound_branch(self, operation, possible_master_transports=None):
 
434
    def _check_bound_branch(self, possible_master_transports=None):
468
435
        """Check to see if the local branch is bound.
469
436
 
470
437
        If it is bound, then most of the commit will actually be
505
472
        # so grab the lock
506
473
        self.bound_branch = self.branch
507
474
        self.master_branch.lock_write()
508
 
        operation.add_cleanup(self.master_branch.unlock)
 
475
        self.master_locked = True
509
476
 
510
477
    def _check_out_of_date_tree(self):
511
478
        """Check that the working tree is up to date.
596
563
                     old_revno, old_revid, new_revno, self.rev_id,
597
564
                     tree_delta, future_tree)
598
565
 
 
566
    def _cleanup(self):
 
567
        """Cleanup any open locks, progress bars etc."""
 
568
        cleanups = [self._cleanup_bound_branch,
 
569
                    self.basis_tree.unlock,
 
570
                    self.work_tree.unlock,
 
571
                    self.pb.finished]
 
572
        found_exception = None
 
573
        for cleanup in cleanups:
 
574
            try:
 
575
                cleanup()
 
576
            # we want every cleanup to run no matter what.
 
577
            # so we have a catchall here, but we will raise the
 
578
            # last encountered exception up the stack: and
 
579
            # typically this will be useful enough.
 
580
            except Exception, e:
 
581
                found_exception = e
 
582
        if found_exception is not None:
 
583
            # don't do a plan raise, because the last exception may have been
 
584
            # trashed, e is our sure-to-work exception even though it loses the
 
585
            # full traceback. XXX: RBC 20060421 perhaps we could check the
 
586
            # exc_info and if its the same one do a plain raise otherwise
 
587
            # 'raise e' as we do now.
 
588
            raise e
 
589
 
 
590
    def _cleanup_bound_branch(self):
 
591
        """Executed at the end of a try/finally to cleanup a bound branch.
 
592
 
 
593
        If the branch wasn't bound, this is a no-op.
 
594
        If it was, it resents self.branch to the local branch, instead
 
595
        of being the master.
 
596
        """
 
597
        if not self.bound_branch:
 
598
            return
 
599
        if self.master_locked:
 
600
            self.master_branch.unlock()
 
601
 
599
602
    def _gather_parents(self):
600
603
        """Record the parents of a merge for merge detection."""
601
604
        # TODO: Make sure that this list doesn't contain duplicate
616
619
        """Update the commit builder with the data about what has changed.
617
620
        """
618
621
        exclude = self.exclude
619
 
        specific_files = self.specific_files
 
622
        specific_files = self.specific_files or []
620
623
        mutter("Selecting files for commit with filter %s", specific_files)
621
624
 
622
625
        self._check_strict()
623
626
        if self.use_record_iter_changes:
624
 
            iter_changes = self.work_tree.iter_changes(self.basis_tree,
625
 
                specific_files=specific_files)
 
627
            iter_changes = self.work_tree.iter_changes(self.basis_tree)
626
628
            iter_changes = self._filter_iter_changes(iter_changes)
627
629
            for file_id, path, fs_hash in self.builder.record_iter_changes(
628
630
                self.work_tree, self.basis_revid, iter_changes):
800
802
                # _update_builder_with_changes.
801
803
                continue
802
804
            content_summary = self.work_tree.path_content_summary(path)
803
 
            kind = content_summary[0]
804
805
            # Note that when a filter of specific files is given, we must only
805
806
            # skip/record deleted files matching that filter.
806
807
            if not specific_files or is_inside_any(specific_files, path):
807
 
                if kind == 'missing':
 
808
                if content_summary[0] == 'missing':
808
809
                    if not deleted_paths:
809
810
                        # path won't have been split yet.
810
811
                        path_segments = splitpath(path)
817
818
                    continue
818
819
            # TODO: have the builder do the nested commit just-in-time IF and
819
820
            # only if needed.
820
 
            if kind == 'tree-reference':
 
821
            if content_summary[0] == 'tree-reference':
821
822
                # enforce repository nested tree policy.
822
823
                if (not self.work_tree.supports_tree_reference() or
823
824
                    # repository does not support it either.
824
825
                    not self.branch.repository._format.supports_tree_reference):
825
 
                    kind = 'directory'
826
 
                    content_summary = (kind, None, None, None)
827
 
                elif self.recursive == 'down':
 
826
                    content_summary = ('directory',) + content_summary[1:]
 
827
            kind = content_summary[0]
 
828
            # TODO: specific_files filtering before nested tree processing
 
829
            if kind == 'tree-reference':
 
830
                if self.recursive == 'down':
828
831
                    nested_revision_id = self._commit_nested_tree(
829
832
                        file_id, path)
830
 
                    content_summary = (kind, None, None, nested_revision_id)
 
833
                    content_summary = content_summary[:3] + (
 
834
                        nested_revision_id,)
831
835
                else:
832
 
                    nested_revision_id = self.work_tree.get_reference_revision(file_id)
833
 
                    content_summary = (kind, None, None, nested_revision_id)
 
836
                    content_summary = content_summary[:3] + (
 
837
                        self.work_tree.get_reference_revision(file_id),)
834
838
 
835
839
            # Record an entry for this item
836
840
            # Note: I don't particularly want to have the existing_ie