~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-09-23 06:54:14 UTC
  • mfrom: (4695.5.9 remove-logging)
  • Revision ID: pqm@pqm.ubuntu.com-20090923065414-vue8c7speunw9wbq
(mbp) deprecate trace.info, error, show_error

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
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,
 
65
    xml_serializer,
58
66
    )
59
67
from bzrlib.branch import Branch
60
 
from bzrlib.cleanup import OperationWithCleanups
61
68
import bzrlib.config
62
69
from bzrlib.errors import (BzrError, PointlessCommit,
63
70
                           ConflictsInTree,
64
71
                           StrictCommitFailed
65
72
                           )
66
73
from bzrlib.osutils import (get_user_encoding,
67
 
                            is_inside_any,
 
74
                            kind_marker, isdir,isfile, is_inside_any,
 
75
                            is_inside_or_parent_of_any,
68
76
                            minimum_path_selection,
 
77
                            quotefn, sha_file, split_lines,
69
78
                            splitpath,
70
79
                            )
71
 
from bzrlib.trace import mutter, note, is_quiet
 
80
from bzrlib.testament import Testament
 
81
from bzrlib.trace import mutter, note, warning, is_quiet
72
82
from bzrlib.inventory import Inventory, InventoryEntry, make_entry
73
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
74
88
from bzrlib.urlutils import unescape_for_display
75
89
import bzrlib.ui
76
90
 
132
146
 
133
147
    def completed(self, revno, rev_id):
134
148
        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
149
 
142
150
    def deleted(self, path):
143
151
        self._note('deleted %s', path)
226
234
            commit. Pending changes to excluded files will be ignored by the
227
235
            commit.
228
236
        """
229
 
        operation = OperationWithCleanups(self._commit)
230
 
        self.revprops = revprops or {}
231
 
        # XXX: Can be set on __init__ or passed in - this is a bit ugly.
232
 
        self.config = config or self.config
233
 
        return operation.run(
234
 
               message=message,
235
 
               timestamp=timestamp,
236
 
               timezone=timezone,
237
 
               committer=committer,
238
 
               specific_files=specific_files,
239
 
               rev_id=rev_id,
240
 
               allow_pointless=allow_pointless,
241
 
               strict=strict,
242
 
               verbose=verbose,
243
 
               working_tree=working_tree,
244
 
               local=local,
245
 
               reporter=reporter,
246
 
               message_callback=message_callback,
247
 
               recursive=recursive,
248
 
               exclude=exclude,
249
 
               possible_master_transports=possible_master_transports)
250
 
 
251
 
    def _commit(self, operation, message, timestamp, timezone, committer,
252
 
            specific_files, rev_id, allow_pointless, strict, verbose,
253
 
            working_tree, local, reporter, message_callback, recursive,
254
 
            exclude, possible_master_transports):
255
237
        mutter('preparing to commit')
256
238
 
257
239
        if working_tree is None:
280
262
            self.exclude = []
281
263
        self.local = local
282
264
        self.master_branch = None
 
265
        self.master_locked = False
283
266
        self.recursive = recursive
284
267
        self.rev_id = None
285
268
        # self.specific_files is None to indicate no filter, or any iterable to
291
274
            self.specific_files = None
292
275
            
293
276
        self.allow_pointless = allow_pointless
 
277
        self.revprops = revprops
294
278
        self.message_callback = message_callback
295
279
        self.timestamp = timestamp
296
280
        self.timezone = timezone
299
283
        self.verbose = verbose
300
284
 
301
285
        self.work_tree.lock_write()
302
 
        operation.add_cleanup(self.work_tree.unlock)
303
286
        self.parents = self.work_tree.get_parent_ids()
304
287
        # We can use record_iter_changes IFF iter_changes is compatible with
305
288
        # the command line parameters, and the repository has fast delta
310
293
            (self.branch.repository._format.fast_deltas or
311
294
             len(self.parents) < 2))
312
295
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
313
 
        operation.add_cleanup(self.pb.finished)
314
296
        self.basis_revid = self.work_tree.last_revision()
315
297
        self.basis_tree = self.work_tree.basis_tree()
316
298
        self.basis_tree.lock_read()
317
 
        operation.add_cleanup(self.basis_tree.unlock)
318
 
        # Cannot commit with conflicts present.
319
 
        if len(self.work_tree.conflicts()) > 0:
320
 
            raise ConflictsInTree
321
 
 
322
 
        # Setup the bound branch variables as needed.
323
 
        self._check_bound_branch(operation, possible_master_transports)
324
 
 
325
 
        # Check that the working tree is up to date
326
 
        old_revno, new_revno = self._check_out_of_date_tree()
327
 
 
328
 
        # Complete configuration setup
329
 
        if reporter is not None:
330
 
            self.reporter = reporter
331
 
        elif self.reporter is None:
332
 
            self.reporter = self._select_reporter()
333
 
        if self.config is None:
334
 
            self.config = self.branch.get_config()
335
 
 
336
 
        self._set_specific_file_ids()
337
 
 
338
 
        # Setup the progress bar. As the number of files that need to be
339
 
        # committed in unknown, progress is reported as stages.
340
 
        # We keep track of entries separately though and include that
341
 
        # information in the progress bar during the relevant stages.
342
 
        self.pb_stage_name = ""
343
 
        self.pb_stage_count = 0
344
 
        self.pb_stage_total = 5
345
 
        if self.bound_branch:
346
 
            self.pb_stage_total += 1
347
 
        self.pb.show_pct = False
348
 
        self.pb.show_spinner = False
349
 
        self.pb.show_eta = False
350
 
        self.pb.show_count = True
351
 
        self.pb.show_bar = True
352
 
 
353
 
        self._gather_parents()
354
 
        # After a merge, a selected file commit is not supported.
355
 
        # See 'bzr help merge' for an explanation as to why.
356
 
        if len(self.parents) > 1 and self.specific_files is not None:
357
 
            raise errors.CannotCommitSelectedFileMerge(self.specific_files)
358
 
        # Excludes are a form of selected file commit.
359
 
        if len(self.parents) > 1 and self.exclude:
360
 
            raise errors.CannotCommitSelectedFileMerge(self.exclude)
361
 
 
362
 
        # Collect the changes
363
 
        self._set_progress_stage("Collecting changes", counter=True)
364
 
        self.builder = self.branch.get_commit_builder(self.parents,
365
 
            self.config, timestamp, timezone, committer, self.revprops, rev_id)
366
 
 
367
299
        try:
368
 
            self.builder.will_record_deletes()
369
 
            # find the location being committed to
370
 
            if self.bound_branch:
371
 
                master_location = self.master_branch.base
372
 
            else:
373
 
                master_location = self.branch.base
374
 
 
375
 
            # report the start of the commit
376
 
            self.reporter.started(new_revno, self.rev_id, master_location)
377
 
 
378
 
            self._update_builder_with_changes()
379
 
            self._check_pointless()
380
 
 
381
 
            # TODO: Now the new inventory is known, check for conflicts.
382
 
            # ADHB 2006-08-08: If this is done, populate_new_inv should not add
383
 
            # weave lines, because nothing should be recorded until it is known
384
 
            # that commit will succeed.
385
 
            self._set_progress_stage("Saving data locally")
386
 
            self.builder.finish_inventory()
387
 
 
388
 
            # Prompt the user for a commit message if none provided
389
 
            message = message_callback(self)
390
 
            self.message = message
391
 
 
392
 
            # Add revision data to the local branch
393
 
            self.rev_id = self.builder.commit(self.message)
394
 
 
395
 
        except Exception, e:
396
 
            mutter("aborting commit write group because of exception:")
397
 
            trace.log_exception_quietly()
398
 
            note("aborting commit write group: %r" % (e,))
399
 
            self.builder.abort()
400
 
            raise
401
 
 
402
 
        self._process_pre_hooks(old_revno, new_revno)
403
 
 
404
 
        # Upload revision data to the master.
405
 
        # this will propagate merged revisions too if needed.
406
 
        if self.bound_branch:
407
 
            self._set_progress_stage("Uploading data to master branch")
408
 
            # 'commit' to the master first so a timeout here causes the
409
 
            # local branch to be out of date
410
 
            self.master_branch.import_last_revision_info(
411
 
                self.branch.repository, new_revno, self.rev_id)
412
 
 
413
 
        # and now do the commit locally.
414
 
        self.branch.set_last_revision_info(new_revno, self.rev_id)
415
 
 
416
 
        # Make the working tree be up to date with the branch. This
417
 
        # includes automatic changes scheduled to be made to the tree, such
418
 
        # as updating its basis and unversioning paths that were missing.
419
 
        self.work_tree.unversion(self.deleted_ids)
420
 
        self._set_progress_stage("Updating the working tree")
421
 
        self.work_tree.update_basis_by_delta(self.rev_id,
422
 
             self.builder.get_basis_delta())
423
 
        self.reporter.completed(new_revno, self.rev_id)
424
 
        self._process_post_hooks(old_revno, new_revno)
 
300
            # Cannot commit with conflicts present.
 
301
            if len(self.work_tree.conflicts()) > 0:
 
302
                raise ConflictsInTree
 
303
 
 
304
            # Setup the bound branch variables as needed.
 
305
            self._check_bound_branch(possible_master_transports)
 
306
 
 
307
            # Check that the working tree is up to date
 
308
            old_revno, new_revno = self._check_out_of_date_tree()
 
309
 
 
310
            # Complete configuration setup
 
311
            if reporter is not None:
 
312
                self.reporter = reporter
 
313
            elif self.reporter is None:
 
314
                self.reporter = self._select_reporter()
 
315
            if self.config is None:
 
316
                self.config = self.branch.get_config()
 
317
 
 
318
            self._set_specific_file_ids()
 
319
 
 
320
            # Setup the progress bar. As the number of files that need to be
 
321
            # committed in unknown, progress is reported as stages.
 
322
            # We keep track of entries separately though and include that
 
323
            # information in the progress bar during the relevant stages.
 
324
            self.pb_stage_name = ""
 
325
            self.pb_stage_count = 0
 
326
            self.pb_stage_total = 5
 
327
            if self.bound_branch:
 
328
                self.pb_stage_total += 1
 
329
            self.pb.show_pct = False
 
330
            self.pb.show_spinner = False
 
331
            self.pb.show_eta = False
 
332
            self.pb.show_count = True
 
333
            self.pb.show_bar = True
 
334
 
 
335
            self._gather_parents()
 
336
            # After a merge, a selected file commit is not supported.
 
337
            # See 'bzr help merge' for an explanation as to why.
 
338
            if len(self.parents) > 1 and self.specific_files is not None:
 
339
                raise errors.CannotCommitSelectedFileMerge(self.specific_files)
 
340
            # Excludes are a form of selected file commit.
 
341
            if len(self.parents) > 1 and self.exclude:
 
342
                raise errors.CannotCommitSelectedFileMerge(self.exclude)
 
343
 
 
344
            # Collect the changes
 
345
            self._set_progress_stage("Collecting changes", counter=True)
 
346
            self.builder = self.branch.get_commit_builder(self.parents,
 
347
                self.config, timestamp, timezone, committer, revprops, rev_id)
 
348
 
 
349
            try:
 
350
                self.builder.will_record_deletes()
 
351
                # find the location being committed to
 
352
                if self.bound_branch:
 
353
                    master_location = self.master_branch.base
 
354
                else:
 
355
                    master_location = self.branch.base
 
356
 
 
357
                # report the start of the commit
 
358
                self.reporter.started(new_revno, self.rev_id, master_location)
 
359
 
 
360
                self._update_builder_with_changes()
 
361
                self._check_pointless()
 
362
 
 
363
                # TODO: Now the new inventory is known, check for conflicts.
 
364
                # ADHB 2006-08-08: If this is done, populate_new_inv should not add
 
365
                # weave lines, because nothing should be recorded until it is known
 
366
                # that commit will succeed.
 
367
                self._set_progress_stage("Saving data locally")
 
368
                self.builder.finish_inventory()
 
369
 
 
370
                # Prompt the user for a commit message if none provided
 
371
                message = message_callback(self)
 
372
                self.message = message
 
373
 
 
374
                # Add revision data to the local branch
 
375
                self.rev_id = self.builder.commit(self.message)
 
376
 
 
377
            except Exception, e:
 
378
                mutter("aborting commit write group because of exception:")
 
379
                trace.log_exception_quietly()
 
380
                note("aborting commit write group: %r" % (e,))
 
381
                self.builder.abort()
 
382
                raise
 
383
 
 
384
            self._process_pre_hooks(old_revno, new_revno)
 
385
 
 
386
            # Upload revision data to the master.
 
387
            # this will propagate merged revisions too if needed.
 
388
            if self.bound_branch:
 
389
                self._set_progress_stage("Uploading data to master branch")
 
390
                # 'commit' to the master first so a timeout here causes the
 
391
                # local branch to be out of date
 
392
                self.master_branch.import_last_revision_info(
 
393
                    self.branch.repository, new_revno, self.rev_id)
 
394
 
 
395
            # and now do the commit locally.
 
396
            self.branch.set_last_revision_info(new_revno, self.rev_id)
 
397
 
 
398
            # Make the working tree be up to date with the branch. This
 
399
            # includes automatic changes scheduled to be made to the tree, such
 
400
            # as updating its basis and unversioning paths that were missing.
 
401
            self.work_tree.unversion(self.deleted_ids)
 
402
            self._set_progress_stage("Updating the working tree")
 
403
            self.work_tree.update_basis_by_delta(self.rev_id,
 
404
                 self.builder.get_basis_delta())
 
405
            self.reporter.completed(new_revno, self.rev_id)
 
406
            self._process_post_hooks(old_revno, new_revno)
 
407
        finally:
 
408
            self._cleanup()
425
409
        return self.rev_id
426
410
 
427
411
    def _select_reporter(self):
449
433
            return
450
434
        raise PointlessCommit()
451
435
 
452
 
    def _check_bound_branch(self, operation, possible_master_transports=None):
 
436
    def _check_bound_branch(self, possible_master_transports=None):
453
437
        """Check to see if the local branch is bound.
454
438
 
455
439
        If it is bound, then most of the commit will actually be
490
474
        # so grab the lock
491
475
        self.bound_branch = self.branch
492
476
        self.master_branch.lock_write()
493
 
        operation.add_cleanup(self.master_branch.unlock)
 
477
        self.master_locked = True
494
478
 
495
479
    def _check_out_of_date_tree(self):
496
480
        """Check that the working tree is up to date.
581
565
                     old_revno, old_revid, new_revno, self.rev_id,
582
566
                     tree_delta, future_tree)
583
567
 
 
568
    def _cleanup(self):
 
569
        """Cleanup any open locks, progress bars etc."""
 
570
        cleanups = [self._cleanup_bound_branch,
 
571
                    self.basis_tree.unlock,
 
572
                    self.work_tree.unlock,
 
573
                    self.pb.finished]
 
574
        found_exception = None
 
575
        for cleanup in cleanups:
 
576
            try:
 
577
                cleanup()
 
578
            # we want every cleanup to run no matter what.
 
579
            # so we have a catchall here, but we will raise the
 
580
            # last encountered exception up the stack: and
 
581
            # typically this will be useful enough.
 
582
            except Exception, e:
 
583
                found_exception = e
 
584
        if found_exception is not None:
 
585
            # don't do a plan raise, because the last exception may have been
 
586
            # trashed, e is our sure-to-work exception even though it loses the
 
587
            # full traceback. XXX: RBC 20060421 perhaps we could check the
 
588
            # exc_info and if its the same one do a plain raise otherwise
 
589
            # 'raise e' as we do now.
 
590
            raise e
 
591
 
 
592
    def _cleanup_bound_branch(self):
 
593
        """Executed at the end of a try/finally to cleanup a bound branch.
 
594
 
 
595
        If the branch wasn't bound, this is a no-op.
 
596
        If it was, it resents self.branch to the local branch, instead
 
597
        of being the master.
 
598
        """
 
599
        if not self.bound_branch:
 
600
            return
 
601
        if self.master_locked:
 
602
            self.master_branch.unlock()
 
603
 
584
604
    def _gather_parents(self):
585
605
        """Record the parents of a merge for merge detection."""
586
606
        # TODO: Make sure that this list doesn't contain duplicate