/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: Michael Ellerman
  • Date: 2006-05-31 08:44:29 UTC
  • mto: (1711.2.63 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1792.
  • Revision ID: michael@ellerman.id.au-20060531084429-35e5429abda9f560
Add optional location to ancestry and fix behaviour for checkouts.

This adds an optional location parameter to the ancestry command. It also
changes the behaviour of ancestry on checkouts such that if they have
been created with a subset of the branch history, only the subset is
shown by 'bzr ancestry'. Tests for all of that as well.

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
 
414
460
 
415
461
    def _escape_commit_message(self):
416
462
        """Replace xml-incompatible control characters."""
 
463
        # FIXME: RBC 20060419 this should be done by the revision
 
464
        # serialiser not by commit. Then we can also add an unescaper
 
465
        # in the deserializer and start roundtripping revision messages
 
466
        # precisely. See repository_implementations/test_repository.py
 
467
        
417
468
        # Python strings can include characters that can't be
418
469
        # represented in well-formed XML; escape characters that
419
470
        # aren't listed in the XML specification
498
549
        # XXX: Need to think more here about when the user has
499
550
        # made a specific decision on a particular value -- c.f.
500
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()
501
556
        for path, ie in self.new_inv.iter_entries():
 
557
            self._emit_progress_update()
502
558
            previous_entries = ie.find_previous_heads(
503
559
                self.parent_invs,
504
560
                self.weave_store,
505
561
                self.branch.repository.get_transaction())
506
562
            if ie.revision is None:
507
 
                change = ie.snapshot(self.rev_id, path, previous_entries,
508
 
                                     self.work_tree, self.weave_store,
509
 
                                     self.branch.get_transaction())
510
 
            else:
511
 
                change = "unchanged"
512
 
            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)
513
581
 
514
582
    def _populate_new_inv(self):
515
583
        """Build revision inventory.
523
591
        """
524
592
        mutter("Selecting files for commit with filter %s", self.specific_files)
525
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()
526
597
        for path, new_ie in self.work_inv.iter_entries():
 
598
            self._emit_progress_update()
527
599
            file_id = new_ie.file_id
528
600
            mutter('check %s {%s}', path, new_ie.file_id)
529
601
            if self.specific_files:
549
621
            mutter('%s selected for commit', path)
550
622
            self._select_entry(new_ie)
551
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
 
552
629
    def _select_entry(self, new_ie):
553
630
        """Make new_ie be considered for committing."""
554
631
        ie = new_ie.copy()
560
637
        """Carry the file unchanged from the basis revision."""
561
638
        if self.basis_inv.has_id(file_id):
562
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
563
643
 
564
644
    def _report_deletes(self):
565
 
        for file_id in self.basis_inv:
566
 
            if file_id not in self.new_inv:
567
 
                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)
568
648
 
569
649
def _gen_revision_id(config, when):
570
650
    """Return new revision-id."""