~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: John Arbash Meinel
  • Date: 2006-05-19 00:35:14 UTC
  • mfrom: (1714 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1724.
  • Revision ID: john@arbash-meinel.com-20060519003514-e67fbb17be0c0f82
[merge] bzr.dev 1714

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
89
89
from bzrlib.testament import Testament
90
90
from bzrlib.trace import mutter, note, warning
91
91
from bzrlib.xml5 import serializer_v5
92
 
from bzrlib.inventory import Inventory, ROOT_ID
 
92
from bzrlib.inventory import Inventory, ROOT_ID, InventoryEntry
93
93
from bzrlib.symbol_versioning import *
94
94
from bzrlib.workingtree import WorkingTree
95
95
 
124
124
    def missing(self, path):
125
125
        pass
126
126
 
 
127
    def renamed(self, change, old_path, new_path):
 
128
        pass
 
129
 
127
130
 
128
131
class ReportCommitToLog(NullCommitReporter):
129
132
 
 
133
    # this may be more useful if 'note' was replaced by an overridable
 
134
    # method on self, which would allow more trivial subclassing.
 
135
    # alternative, a callable could be passed in, allowing really trivial
 
136
    # reuse for some uis. RBC 20060511
 
137
 
130
138
    def snapshot_change(self, change, path):
131
139
        if change == 'unchanged':
132
140
            return
144
152
    def missing(self, path):
145
153
        note('missing %s', path)
146
154
 
 
155
    def renamed(self, change, old_path, new_path):
 
156
        note('%s %s => %s', change, old_path, new_path)
 
157
 
147
158
 
148
159
class Commit(object):
149
160
    """Task of committing a new revision.
245
256
            self.reporter = reporter
246
257
 
247
258
        self.work_tree.lock_write()
 
259
        self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
248
260
        try:
 
261
            # Cannot commit with conflicts present.
 
262
            if len(self.work_tree.conflicts())>0:
 
263
                raise ConflictsInTree
 
264
 
249
265
            # setup the bound branch variables as needed.
250
266
            self._check_bound_branch()
251
267
 
293
309
            self.work_inv = self.work_tree.inventory
294
310
            self.basis_tree = self.work_tree.basis_tree()
295
311
            self.basis_inv = self.basis_tree.inventory
 
312
            # one to finish, one for rev and inventory, and one for each
 
313
            # inventory entry, and the same for the new inventory.
 
314
            # note that this estimate is too long when we do a partial tree
 
315
            # commit which excludes some new files from being considered.
 
316
            # The estimate is corrected when we populate the new inv.
 
317
            self.pb_total = len(self.basis_inv) + len(self.work_inv) + 3 - 1
 
318
            self.pb_count = 0
296
319
 
297
320
            self._gather_parents()
298
321
            if len(self.parents) > 1 and self.specific_files:
310
333
                    or self.new_inv != self.basis_inv):
311
334
                raise PointlessCommit()
312
335
 
313
 
            if len(self.work_tree.conflicts())>0:
314
 
                raise ConflictsInTree
315
 
 
 
336
            self._emit_progress_update()
316
337
            self.inv_sha1 = self.branch.repository.add_inventory(
317
338
                self.rev_id,
318
339
                self.new_inv,
319
340
                self.present_parents
320
341
                )
 
342
            self._emit_progress_update()
321
343
            self._make_revision()
322
344
            # revision data is in the local branch now.
323
345
            
347
369
                                  {'branch':self.branch,
348
370
                                   'bzrlib':bzrlib,
349
371
                                   'rev_id':self.rev_id})
 
372
            self._emit_progress_update()
350
373
        finally:
351
 
            self._cleanup_bound_branch()
352
 
            self.work_tree.unlock()
 
374
            self._cleanup()
353
375
 
354
376
    def _check_bound_branch(self):
355
377
        """Check to see if the local branch is bound.
401
423
####                self.master_branch.repository.fetch(self.bound_branch.repository,
402
424
####                                                    revision_id=revision_id)
403
425
 
 
426
    def _cleanup(self):
 
427
        """Cleanup any open locks, progress bars etc."""
 
428
        cleanups = [self._cleanup_bound_branch,
 
429
                    self.work_tree.unlock,
 
430
                    self.pb.finished]
 
431
        found_exception = None
 
432
        for cleanup in cleanups:
 
433
            try:
 
434
                cleanup()
 
435
            # we want every cleanup to run no matter what.
 
436
            # so we have a catchall here, but we will raise the
 
437
            # last encountered exception up the stack: and
 
438
            # typically this will be useful enough.
 
439
            except Exception, e:
 
440
                found_exception = e
 
441
        if found_exception is not None: 
 
442
            # dont do a plan raise, because the last exception may have been
 
443
            # trashed, e is our sure-to-work exception even though it loses the
 
444
            # full traceback. XXX: RBC 20060421 perhaps we could check the
 
445
            # exc_info and if its the same one do a plain raise otherwise 
 
446
            # 'raise e' as we do now.
 
447
            raise e
 
448
 
404
449
    def _cleanup_bound_branch(self):
405
450
        """Executed at the end of a try/finally to cleanup a bound branch.
406
451
 
504
549
        # XXX: Need to think more here about when the user has
505
550
        # made a specific decision on a particular value -- c.f.
506
551
        # mark-merge.  
 
552
 
 
553
        # iter_entries does not visit the ROOT_ID node so we need to call
 
554
        # self._emit_progress_update once by hand.
 
555
        self._emit_progress_update()
507
556
        for path, ie in self.new_inv.iter_entries():
 
557
            self._emit_progress_update()
508
558
            previous_entries = ie.find_previous_heads(
509
559
                self.parent_invs,
510
560
                self.weave_store,
511
561
                self.branch.repository.get_transaction())
512
562
            if ie.revision is None:
513
 
                change = ie.snapshot(self.rev_id, path, previous_entries,
514
 
                                     self.work_tree, self.weave_store,
515
 
                                     self.branch.repository.get_transaction())
516
 
            else:
517
 
                change = "unchanged"
518
 
            self.reporter.snapshot_change(change, path)
 
563
                # we are creating a new revision for ie in the history store
 
564
                # and inventory.
 
565
                ie.snapshot(self.rev_id, path, previous_entries,
 
566
                    self.work_tree, self.weave_store,
 
567
                    self.branch.repository.get_transaction())
 
568
            # describe the nature of the change that has occured relative to
 
569
            # the basis inventory.
 
570
            if (self.basis_inv.has_id(ie.file_id)):
 
571
                basis_ie = self.basis_inv[ie.file_id]
 
572
            else:
 
573
                basis_ie = None
 
574
            change = ie.describe_change(basis_ie, ie)
 
575
            if change in (InventoryEntry.RENAMED, 
 
576
                InventoryEntry.MODIFIED_AND_RENAMED):
 
577
                old_path = self.basis_inv.id2path(ie.file_id)
 
578
                self.reporter.renamed(change, old_path, path)
 
579
            else:
 
580
                self.reporter.snapshot_change(change, path)
519
581
 
520
582
    def _populate_new_inv(self):
521
583
        """Build revision inventory.
529
591
        """
530
592
        mutter("Selecting files for commit with filter %s", self.specific_files)
531
593
        self.new_inv = Inventory(revision_id=self.rev_id)
 
594
        # iter_entries does not visit the ROOT_ID node so we need to call
 
595
        # self._emit_progress_update once by hand.
 
596
        self._emit_progress_update()
532
597
        for path, new_ie in self.work_inv.iter_entries():
 
598
            self._emit_progress_update()
533
599
            file_id = new_ie.file_id
534
600
            mutter('check %s {%s}', path, new_ie.file_id)
535
601
            if self.specific_files:
555
621
            mutter('%s selected for commit', path)
556
622
            self._select_entry(new_ie)
557
623
 
 
624
    def _emit_progress_update(self):
 
625
        """Emit an update to the progress bar."""
 
626
        self.pb.update("Committing", self.pb_count, self.pb_total)
 
627
        self.pb_count += 1
 
628
 
558
629
    def _select_entry(self, new_ie):
559
630
        """Make new_ie be considered for committing."""
560
631
        ie = new_ie.copy()
566
637
        """Carry the file unchanged from the basis revision."""
567
638
        if self.basis_inv.has_id(file_id):
568
639
            self.new_inv.add(self.basis_inv[file_id].copy())
 
640
        else:
 
641
            # this entry is new and not being committed
 
642
            self.pb_total -= 1
569
643
 
570
644
    def _report_deletes(self):
571
 
        for file_id in self.basis_inv:
572
 
            if file_id not in self.new_inv:
573
 
                self.reporter.deleted(self.basis_inv.id2path(file_id))
 
645
        for path, ie in self.basis_inv.iter_entries():
 
646
            if ie.file_id not in self.new_inv:
 
647
                self.reporter.deleted(path)
574
648
 
575
649
def _gen_revision_id(config, when):
576
650
    """Return new revision-id."""