~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-06 12:06:20 UTC
  • mfrom: (1740 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060606120620-50066b0951e4ef7c
merge bzr.dev 1740

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:
299
 
                raise NotImplementedError('selected-file commit of merges is not supported yet')
 
322
                raise NotImplementedError('selected-file commit of merges is not supported yet: files %r',
 
323
                        self.specific_files)
300
324
            self._check_parents_present()
301
325
            
302
326
            self._remove_deleted()
309
333
                    or self.new_inv != self.basis_inv):
310
334
                raise PointlessCommit()
311
335
 
312
 
            if len(self.work_tree.conflicts())>0:
313
 
                raise ConflictsInTree
314
 
 
 
336
            self._emit_progress_update()
315
337
            self.inv_sha1 = self.branch.repository.add_inventory(
316
338
                self.rev_id,
317
339
                self.new_inv,
318
340
                self.present_parents
319
341
                )
 
342
            self._emit_progress_update()
320
343
            self._make_revision()
321
344
            # revision data is in the local branch now.
322
345
            
346
369
                                  {'branch':self.branch,
347
370
                                   'bzrlib':bzrlib,
348
371
                                   'rev_id':self.rev_id})
 
372
            self._emit_progress_update()
349
373
        finally:
350
 
            self._cleanup_bound_branch()
351
 
            self.work_tree.unlock()
 
374
            self._cleanup()
352
375
 
353
376
    def _check_bound_branch(self):
354
377
        """Check to see if the local branch is bound.
400
423
####                self.master_branch.repository.fetch(self.bound_branch.repository,
401
424
####                                                    revision_id=revision_id)
402
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
 
403
449
    def _cleanup_bound_branch(self):
404
450
        """Executed at the end of a try/finally to cleanup a bound branch.
405
451
 
503
549
        # XXX: Need to think more here about when the user has
504
550
        # made a specific decision on a particular value -- c.f.
505
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()
506
556
        for path, ie in self.new_inv.iter_entries():
 
557
            self._emit_progress_update()
507
558
            previous_entries = ie.find_previous_heads(
508
559
                self.parent_invs,
509
560
                self.weave_store,
510
561
                self.branch.repository.get_transaction())
511
562
            if ie.revision is None:
512
 
                change = ie.snapshot(self.rev_id, path, previous_entries,
513
 
                                     self.work_tree, self.weave_store,
514
 
                                     self.branch.get_transaction())
515
 
            else:
516
 
                change = "unchanged"
517
 
            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)
518
581
 
519
582
    def _populate_new_inv(self):
520
583
        """Build revision inventory.
528
591
        """
529
592
        mutter("Selecting files for commit with filter %s", self.specific_files)
530
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()
531
597
        for path, new_ie in self.work_inv.iter_entries():
 
598
            self._emit_progress_update()
532
599
            file_id = new_ie.file_id
533
600
            mutter('check %s {%s}', path, new_ie.file_id)
534
601
            if self.specific_files:
554
621
            mutter('%s selected for commit', path)
555
622
            self._select_entry(new_ie)
556
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
 
557
629
    def _select_entry(self, new_ie):
558
630
        """Make new_ie be considered for committing."""
559
631
        ie = new_ie.copy()
565
637
        """Carry the file unchanged from the basis revision."""
566
638
        if self.basis_inv.has_id(file_id):
567
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
568
643
 
569
644
    def _report_deletes(self):
570
 
        for file_id in self.basis_inv:
571
 
            if file_id not in self.new_inv:
572
 
                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)
573
648
 
574
649
def _gen_revision_id(config, when):
575
650
    """Return new revision-id."""