~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Robey Pointer
  • Date: 2006-09-03 00:28:18 UTC
  • mfrom: (1981 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1996.
  • Revision ID: robey@lag.net-20060903002818-71ca5c7bfea93a26
merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
394
394
        return pathjoin(self.basedir, filename)
395
395
    
396
396
    def basis_tree(self):
397
 
        """Return RevisionTree for the current last revision."""
 
397
        """Return RevisionTree for the current last revision.
 
398
        
 
399
        If the left most parent is a ghost then the returned tree will be an
 
400
        empty tree - one obtained by calling repository.revision_tree(None).
 
401
        """
398
402
        revision_id = self.last_revision()
399
403
        if revision_id is not None:
400
404
            try:
407
411
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
408
412
                                                revision_id)
409
413
        # FIXME? RBC 20060403 should we cache the inventory here ?
410
 
        return self.branch.repository.revision_tree(revision_id)
 
414
        try:
 
415
            return self.branch.repository.revision_tree(revision_id)
 
416
        except errors.RevisionNotPresent:
 
417
            # the basis tree *may* be a ghost or a low level error may have
 
418
            # occured. If the revision is present, its a problem, if its not
 
419
            # its a ghost.
 
420
            if self.branch.repository.has_revision(revision_id):
 
421
                raise
 
422
            # the basis tree is a ghost so return an empty tree.
 
423
            return self.branch.repository.revision_tree(None)
411
424
 
412
425
    @staticmethod
413
426
    @deprecated_method(zero_eight)
478
491
            parents = []
479
492
        else:
480
493
            parents = [last_rev]
481
 
        other_parents = self.pending_merges()
482
 
        return parents + other_parents
 
494
        try:
 
495
            merges_file = self._control_files.get_utf8('pending-merges')
 
496
        except NoSuchFile:
 
497
            pass
 
498
        else:
 
499
            for l in merges_file.readlines():
 
500
                parents.append(l.rstrip('\n'))
 
501
        return parents
483
502
 
484
503
    def get_root_id(self):
485
504
        """Return the id of this trees root"""
519
538
        if revision_id is None:
520
539
            transform_tree(tree, self)
521
540
        else:
522
 
            # TODO now merge from tree.last_revision to revision
 
541
            # TODO now merge from tree.last_revision to revision (to preserve
 
542
            # user local changes)
523
543
            transform_tree(tree, self)
524
 
            tree.set_last_revision(revision_id)
 
544
            tree.set_parent_ids([revision_id])
525
545
 
526
546
    @needs_write_lock
527
547
    def commit(self, message=None, revprops=None, *args, **kwargs):
644
664
        self._write_inventory(inv)
645
665
 
646
666
    @needs_write_lock
 
667
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
 
668
        """Add revision_id as a parent.
 
669
 
 
670
        This is equivalent to retrieving the current list of parent ids
 
671
        and setting the list to its value plus revision_id.
 
672
 
 
673
        :param revision_id: The revision id to add to the parent list. It may
 
674
        be a ghost revision as long as its not the first parent to be added,
 
675
        or the allow_leftmost_as_ghost parameter is set True.
 
676
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
 
677
        """
 
678
        parents = self.get_parent_ids() + [revision_id]
 
679
        self.set_parent_ids(parents,
 
680
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
 
681
 
 
682
    @needs_write_lock
 
683
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
 
684
        """Add revision_id, tree tuple as a parent.
 
685
 
 
686
        This is equivalent to retrieving the current list of parent trees
 
687
        and setting the list to its value plus parent_tuple. See also
 
688
        add_parent_tree_id - if you only have a parent id available it will be
 
689
        simpler to use that api. If you have the parent already available, using
 
690
        this api is preferred.
 
691
 
 
692
        :param parent_tuple: The (revision id, tree) to add to the parent list.
 
693
            If the revision_id is a ghost, pass None for the tree.
 
694
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
 
695
        """
 
696
        self.set_parent_ids(self.get_parent_ids() + [parent_tuple[0]],
 
697
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
698
 
 
699
    @needs_write_lock
647
700
    def add_pending_merge(self, *revision_ids):
648
701
        # TODO: Perhaps should check at this point that the
649
702
        # history of the revision is actually present?
650
 
        p = self.pending_merges()
 
703
        parents = self.get_parent_ids()
651
704
        updated = False
652
705
        for rev_id in revision_ids:
653
 
            if rev_id in p:
 
706
            if rev_id in parents:
654
707
                continue
655
 
            p.append(rev_id)
 
708
            parents.append(rev_id)
656
709
            updated = True
657
710
        if updated:
658
 
            self.set_pending_merges(p)
 
711
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
659
712
 
660
713
    @needs_read_lock
661
714
    def pending_merges(self):
664
717
        These are revisions that have been merged into the working
665
718
        directory but not yet committed.
666
719
        """
667
 
        try:
668
 
            merges_file = self._control_files.get_utf8('pending-merges')
669
 
        except NoSuchFile:
670
 
            return []
671
 
        p = []
672
 
        for l in merges_file.readlines():
673
 
            p.append(l.rstrip('\n'))
674
 
        return p
 
720
        return self.get_parent_ids()[1:]
 
721
 
 
722
    @needs_write_lock
 
723
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
 
724
        """Set the parent ids to revision_ids.
 
725
        
 
726
        See also set_parent_trees. This api will try to retrieve the tree data
 
727
        for each element of revision_ids from the trees repository. If you have
 
728
        tree data already available, it is more efficient to use
 
729
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
 
730
        an easier API to use.
 
731
 
 
732
        :param revision_ids: The revision_ids to set as the parent ids of this
 
733
            working tree. Any of these may be ghosts.
 
734
        """
 
735
        if len(revision_ids) > 0:
 
736
            leftmost_id = revision_ids[0]
 
737
            if (not allow_leftmost_as_ghost and not
 
738
                self.branch.repository.has_revision(leftmost_id)):
 
739
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
740
            self.set_last_revision(leftmost_id)
 
741
        else:
 
742
            self.set_last_revision(None)
 
743
        merges = revision_ids[1:]
 
744
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
745
 
 
746
    @needs_write_lock
 
747
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
 
748
        """Set the parents of the working tree.
 
749
 
 
750
        :param parents_list: A list of (revision_id, tree) tuples. 
 
751
            If tree is None, then that element is treated as an unreachable
 
752
            parent tree - i.e. a ghost.
 
753
        """
 
754
        # parent trees are not used in current format trees, delegate to
 
755
        # set_parent_ids
 
756
        self.set_parent_ids([rev for (rev, tree) in parents_list],
 
757
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
675
758
 
676
759
    @needs_write_lock
677
760
    def set_pending_merges(self, rev_list):
678
 
        self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
 
761
        parents = self.get_parent_ids()
 
762
        leftmost = parents[:1]
 
763
        new_parents = leftmost + rev_list
 
764
        self.set_parent_ids(new_parents)
679
765
 
680
766
    @needs_write_lock
681
767
    def set_merge_modified(self, modified_hashes):
996
1082
                repository = self.branch.repository
997
1083
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
998
1084
                try:
 
1085
                    new_basis_tree = self.branch.basis_tree()
999
1086
                    merge_inner(self.branch,
1000
 
                                self.branch.basis_tree(),
1001
 
                                basis_tree, 
1002
 
                                this_tree=self, 
 
1087
                                new_basis_tree,
 
1088
                                basis_tree,
 
1089
                                this_tree=self,
1003
1090
                                pb=pb)
1004
1091
                finally:
1005
1092
                    pb.finished()
1006
 
                self.set_last_revision(self.branch.last_revision())
 
1093
                # TODO - dedup parents list with things merged by pull ?
 
1094
                # reuse the revisiontree we merged against to set the new
 
1095
                # tree data.
 
1096
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
1097
                # we have to pull the merge trees out again, because 
 
1098
                # merge_inner has set the ids. - this corner is not yet 
 
1099
                # layered well enough to prevent double handling.
 
1100
                merges = self.get_parent_ids()[1:]
 
1101
                parent_trees.extend([
 
1102
                    (parent, repository.revision_tree(parent)) for
 
1103
                     parent in merges])
 
1104
                self.set_parent_trees(parent_trees)
1007
1105
            return count
1008
1106
        finally:
1009
1107
            source.unlock()
1163
1261
        for regex, mapping in rules:
1164
1262
            match = regex.match(filename)
1165
1263
            if match is not None:
1166
 
                # one or more of the groups in mapping will have a non-None group 
1167
 
                # match.
 
1264
                # one or more of the groups in mapping will have a non-None
 
1265
                # group match.
1168
1266
                groups = match.groups()
1169
1267
                rules = [mapping[group] for group in 
1170
1268
                    mapping if groups[group] is not None]
1326
1424
            old_tree = self.basis_tree()
1327
1425
        conflicts = revert(self, old_tree, filenames, backups, pb)
1328
1426
        if not len(filenames):
1329
 
            self.set_pending_merges([])
 
1427
            self.set_parent_ids(self.get_parent_ids()[:1])
1330
1428
            resolve(self)
1331
1429
        else:
1332
1430
            resolve(self, filenames, ignore_misses=True)
1403
1501
        Do a 'normal' merge of the old branch basis if it is relevant.
1404
1502
        """
1405
1503
        old_tip = self.branch.update()
1406
 
        if old_tip is not None:
1407
 
            self.add_pending_merge(old_tip)
1408
 
        self.branch.lock_read()
1409
 
        try:
1410
 
            result = 0
1411
 
            if self.last_revision() != self.branch.last_revision():
1412
 
                # merge tree state up to new branch tip.
1413
 
                basis = self.basis_tree()
1414
 
                to_tree = self.branch.basis_tree()
1415
 
                result += merge_inner(self.branch,
1416
 
                                      to_tree,
1417
 
                                      basis,
1418
 
                                      this_tree=self)
1419
 
                self.set_last_revision(self.branch.last_revision())
1420
 
            if old_tip and old_tip != self.last_revision():
1421
 
                # our last revision was not the prior branch last revision
1422
 
                # and we have converted that last revision to a pending merge.
1423
 
                # base is somewhere between the branch tip now
1424
 
                # and the now pending merge
1425
 
                from bzrlib.revision import common_ancestor
1426
 
                try:
1427
 
                    base_rev_id = common_ancestor(self.branch.last_revision(),
1428
 
                                                  old_tip,
1429
 
                                                  self.branch.repository)
1430
 
                except errors.NoCommonAncestor:
1431
 
                    base_rev_id = None
1432
 
                base_tree = self.branch.repository.revision_tree(base_rev_id)
1433
 
                other_tree = self.branch.repository.revision_tree(old_tip)
1434
 
                result += merge_inner(self.branch,
1435
 
                                      other_tree,
1436
 
                                      base_tree,
1437
 
                                      this_tree=self)
1438
 
            return result
1439
 
        finally:
1440
 
            self.branch.unlock()
 
1504
        # here if old_tip is not None, it is the old tip of the branch before
 
1505
        # it was updated from the master branch. This should become a pending
 
1506
        # merge in the working tree to preserve the user existing work.  we
 
1507
        # cant set that until we update the working trees last revision to be
 
1508
        # one from the new branch, because it will just get absorbed by the
 
1509
        # parent de-duplication logic.
 
1510
        # 
 
1511
        # We MUST save it even if an error occurs, because otherwise the users
 
1512
        # local work is unreferenced and will appear to have been lost.
 
1513
        # 
 
1514
        result = 0
 
1515
        if self.last_revision() != self.branch.last_revision():
 
1516
            # merge tree state up to new branch tip.
 
1517
            basis = self.basis_tree()
 
1518
            to_tree = self.branch.basis_tree()
 
1519
            result += merge_inner(self.branch,
 
1520
                                  to_tree,
 
1521
                                  basis,
 
1522
                                  this_tree=self)
 
1523
            # TODO - dedup parents list with things merged by pull ?
 
1524
            # reuse the tree we've updated to to set the basis:
 
1525
            parent_trees = [(self.branch.last_revision(), to_tree)]
 
1526
            merges = self.get_parent_ids()[1:]
 
1527
            # Ideally we ask the tree for the trees here, that way the working
 
1528
            # tree can decide whether to give us teh entire tree or give us a
 
1529
            # lazy initialised tree. dirstate for instance will have the trees
 
1530
            # in ram already, whereas a last-revision + basis-inventory tree
 
1531
            # will not, but also does not need them when setting parents.
 
1532
            for parent in merges:
 
1533
                parent_trees.append(
 
1534
                    (parent, self.branch.repository.revision_tree(parent)))
 
1535
            if old_tip is not None:
 
1536
                parent_trees.append(
 
1537
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
 
1538
            self.set_parent_trees(parent_trees)
 
1539
        else:
 
1540
            # the working tree had the same last-revision as the master
 
1541
            # branch did. We may still have pivot local work from the local
 
1542
            # branch into old_tip:
 
1543
            if old_tip is not None:
 
1544
                self.add_parent_tree_id(old_tip)
 
1545
        if old_tip and old_tip != self.last_revision():
 
1546
            # our last revision was not the prior branch last revision
 
1547
            # and we have converted that last revision to a pending merge.
 
1548
            # base is somewhere between the branch tip now
 
1549
            # and the now pending merge
 
1550
            from bzrlib.revision import common_ancestor
 
1551
            try:
 
1552
                base_rev_id = common_ancestor(self.branch.last_revision(),
 
1553
                                              old_tip,
 
1554
                                              self.branch.repository)
 
1555
            except errors.NoCommonAncestor:
 
1556
                base_rev_id = None
 
1557
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
1558
            other_tree = self.branch.repository.revision_tree(old_tip)
 
1559
            result += merge_inner(self.branch,
 
1560
                                  other_tree,
 
1561
                                  base_tree,
 
1562
                                  this_tree=self)
 
1563
        return result
1441
1564
 
1442
1565
    @needs_write_lock
1443
1566
    def _write_inventory(self, inv):
1702
1825
            finally:
1703
1826
                branch.unlock()
1704
1827
        revision = branch.last_revision()
1705
 
        inv = Inventory() 
 
1828
        inv = Inventory()
1706
1829
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
1707
1830
                         branch,
1708
1831
                         inv,
1711
1834
                         _bzrdir=a_bzrdir)
1712
1835
        wt._write_inventory(inv)
1713
1836
        wt.set_root_id(inv.root.file_id)
1714
 
        wt.set_last_revision(revision)
1715
 
        wt.set_pending_merges([])
1716
 
        build_tree(wt.basis_tree(), wt)
 
1837
        basis_tree = branch.repository.revision_tree(revision)
 
1838
        wt.set_parent_trees([(revision, basis_tree)])
 
1839
        build_tree(basis_tree, wt)
1717
1840
        return wt
1718
1841
 
1719
1842
    def __init__(self):
1793
1916
        try:
1794
1917
            wt._write_inventory(inv)
1795
1918
            wt.set_root_id(inv.root.file_id)
1796
 
            wt.set_last_revision(revision_id)
1797
 
            wt.set_pending_merges([])
1798
 
            build_tree(wt.basis_tree(), wt)
 
1919
            basis_tree = branch.repository.revision_tree(revision_id)
 
1920
            if revision_id == bzrlib.revision.NULL_REVISION:
 
1921
                wt.set_parent_trees([])
 
1922
            else:
 
1923
                wt.set_parent_trees([(revision_id, basis_tree)])
 
1924
            build_tree(basis_tree, wt)
1799
1925
        finally:
1800
1926
            wt.unlock()
1801
1927
            control_files.unlock()