~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

(jelmer) Make default Branch implementation oriented to storing just tip
 rather than full history. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
746
746
        """Print `file` to stdout."""
747
747
        raise NotImplementedError(self.print_file)
748
748
 
 
749
    @deprecated_method(deprecated_in((2, 4, 0)))
749
750
    def set_revision_history(self, rev_history):
750
 
        raise NotImplementedError(self.set_revision_history)
 
751
        """See Branch.set_revision_history."""
 
752
        self._set_revision_history(rev_history)
 
753
 
 
754
    @needs_write_lock
 
755
    def _set_revision_history(self, rev_history):
 
756
        if len(rev_history) == 0:
 
757
            revid = _mod_revision.NULL_REVISION
 
758
        else:
 
759
            revid = rev_history[-1]
 
760
        if rev_history != self._lefthand_history(revid):
 
761
            raise errors.NotLefthandHistory(rev_history)
 
762
        self.set_last_revision_info(len(rev_history), revid)
 
763
        self._cache_revision_history(rev_history)
 
764
        for hook in Branch.hooks['set_rh']:
 
765
            hook(self, rev_history)
 
766
 
 
767
    @needs_write_lock
 
768
    def set_last_revision_info(self, revno, revision_id):
 
769
        """Set the last revision of this branch.
 
770
 
 
771
        The caller is responsible for checking that the revno is correct
 
772
        for this revision id.
 
773
 
 
774
        It may be possible to set the branch last revision to an id not
 
775
        present in the repository.  However, branches can also be
 
776
        configured to check constraints on history, in which case this may not
 
777
        be permitted.
 
778
        """
 
779
        raise NotImplementedError(self.last_revision_info)
 
780
 
 
781
    @needs_write_lock
 
782
    def generate_revision_history(self, revision_id, last_rev=None,
 
783
                                  other_branch=None):
 
784
        """See Branch.generate_revision_history"""
 
785
        # FIXME: This shouldn't have to fetch the entire history
 
786
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
787
        revno = len(history)
 
788
        self.set_last_revision_info(revno, revision_id)
 
789
        self._cache_revision_history(history)
751
790
 
752
791
    @needs_write_lock
753
792
    def set_parent(self, url):
983
1022
        :return: A tuple (revno, revision_id).
984
1023
        """
985
1024
        if self._last_revision_info_cache is None:
986
 
            self._last_revision_info_cache = self._last_revision_info()
 
1025
            self._last_revision_info_cache = self._read_last_revision_info()
987
1026
        return self._last_revision_info_cache
988
1027
 
989
 
    def _last_revision_info(self):
990
 
        rh = self.revision_history()
991
 
        revno = len(rh)
992
 
        if revno:
993
 
            return (revno, rh[-1])
994
 
        else:
995
 
            return (0, _mod_revision.NULL_REVISION)
 
1028
    def _read_last_revision_info(self):
 
1029
        raise NotImplementedError(self._read_last_revision_info)
996
1030
 
997
1031
    @deprecated_method(deprecated_in((2, 4, 0)))
998
1032
    def import_last_revision_info(self, source_repo, revno, revid):
2477
2511
        """See Branch.print_file."""
2478
2512
        return self.repository.print_file(file, revision_id)
2479
2513
 
2480
 
    def _write_revision_history(self, history):
2481
 
        """Factored out of set_revision_history.
2482
 
 
2483
 
        This performs the actual writing to disk.
2484
 
        It is intended to be called by BzrBranch5.set_revision_history."""
2485
 
        self._transport.put_bytes(
2486
 
            'revision-history', '\n'.join(history),
2487
 
            mode=self.bzrdir._get_file_mode())
2488
 
 
2489
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2490
 
    def set_revision_history(self, rev_history):
2491
 
        """See Branch.set_revision_history."""
2492
 
        self._set_revision_history(rev_history)
2493
 
 
2494
 
    @needs_write_lock
2495
 
    def _set_revision_history(self, rev_history):
2496
 
        if 'evil' in debug.debug_flags:
2497
 
            mutter_callsite(3, "set_revision_history scales with history.")
2498
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2499
 
        for rev_id in rev_history:
2500
 
            check_not_reserved_id(rev_id)
2501
 
        if Branch.hooks['post_change_branch_tip']:
2502
 
            # Don't calculate the last_revision_info() if there are no hooks
2503
 
            # that will use it.
2504
 
            old_revno, old_revid = self.last_revision_info()
2505
 
        if len(rev_history) == 0:
2506
 
            revid = _mod_revision.NULL_REVISION
2507
 
        else:
2508
 
            revid = rev_history[-1]
2509
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2510
 
        self._write_revision_history(rev_history)
2511
 
        self._clear_cached_state()
2512
 
        self._cache_revision_history(rev_history)
2513
 
        for hook in Branch.hooks['set_rh']:
2514
 
            hook(self, rev_history)
2515
 
        if Branch.hooks['post_change_branch_tip']:
2516
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2517
 
 
2518
 
    def _synchronize_history(self, destination, revision_id):
2519
 
        """Synchronize last revision and revision history between branches.
2520
 
 
2521
 
        This version is most efficient when the destination is also a
2522
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
2523
 
        history is the true lefthand parent history, and all of the revisions
2524
 
        are in the destination's repository.  If not, set_revision_history
2525
 
        will fail.
2526
 
 
2527
 
        :param destination: The branch to copy the history into
2528
 
        :param revision_id: The revision-id to truncate history at.  May
2529
 
          be None to copy complete history.
2530
 
        """
2531
 
        if not isinstance(destination._format, BzrBranchFormat5):
2532
 
            super(BzrBranch, self)._synchronize_history(
2533
 
                destination, revision_id)
2534
 
            return
2535
 
        if revision_id == _mod_revision.NULL_REVISION:
2536
 
            new_history = []
2537
 
        else:
2538
 
            new_history = self.revision_history()
2539
 
        if revision_id is not None and new_history != []:
2540
 
            try:
2541
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2542
 
            except ValueError:
2543
 
                rev = self.repository.get_revision(revision_id)
2544
 
                new_history = rev.get_history(self.repository)[1:]
2545
 
        destination._set_revision_history(new_history)
2546
 
 
2547
2514
    @needs_write_lock
2548
2515
    def set_last_revision_info(self, revno, revision_id):
2549
 
        """Set the last revision of this branch.
2550
 
 
2551
 
        The caller is responsible for checking that the revno is correct
2552
 
        for this revision id.
2553
 
 
2554
 
        It may be possible to set the branch last revision to an id not
2555
 
        present in the repository.  However, branches can also be
2556
 
        configured to check constraints on history, in which case this may not
2557
 
        be permitted.
2558
 
        """
2559
2516
        if not revision_id or not isinstance(revision_id, basestring):
2560
2517
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2561
 
        # this old format stores the full history, but this api doesn't
2562
 
        # provide it, so we must generate, and might as well check it's
2563
 
        # correct
2564
 
        history = self._lefthand_history(revision_id)
2565
 
        if len(history) != revno:
2566
 
            raise AssertionError('%d != %d' % (len(history), revno))
2567
 
        self._set_revision_history(history)
2568
 
 
2569
 
    def _gen_revision_history(self):
2570
 
        history = self._transport.get_bytes('revision-history').split('\n')
2571
 
        if history[-1:] == ['']:
2572
 
            # There shouldn't be a trailing newline, but just in case.
2573
 
            history.pop()
2574
 
        return history
2575
 
 
2576
 
    @needs_write_lock
2577
 
    def generate_revision_history(self, revision_id, last_rev=None,
2578
 
        other_branch=None):
2579
 
        """Create a new revision history that will finish with revision_id.
2580
 
 
2581
 
        :param revision_id: the new tip to use.
2582
 
        :param last_rev: The previous last_revision. If not None, then this
2583
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2584
 
        :param other_branch: The other branch that DivergedBranches should
2585
 
            raise with respect to.
2586
 
        """
2587
 
        self._set_revision_history(self._lefthand_history(revision_id,
2588
 
            last_rev, other_branch))
 
2518
        revision_id = _mod_revision.ensure_null(revision_id)
 
2519
        old_revno, old_revid = self.last_revision_info()
 
2520
        if self._get_append_revisions_only():
 
2521
            self._check_history_violation(revision_id)
 
2522
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2523
        self._write_last_revision_info(revno, revision_id)
 
2524
        self._clear_cached_state()
 
2525
        self._last_revision_info_cache = revno, revision_id
 
2526
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2589
2527
 
2590
2528
    def basis_tree(self):
2591
2529
        """See Branch.basis_tree."""
2616
2554
            self._transport.put_bytes('parent', url + '\n',
2617
2555
                mode=self.bzrdir._get_file_mode())
2618
2556
 
2619
 
 
2620
 
class BzrBranch5(BzrBranch):
2621
 
    """A format 5 branch. This supports new features over plain branches.
2622
 
 
2623
 
    It has support for a master_branch which is the data for bound branches.
2624
 
    """
 
2557
    @needs_write_lock
 
2558
    def unbind(self):
 
2559
        """If bound, unbind"""
 
2560
        return self.set_bound_location(None)
 
2561
 
 
2562
    @needs_write_lock
 
2563
    def bind(self, other):
 
2564
        """Bind this branch to the branch other.
 
2565
 
 
2566
        This does not push or pull data between the branches, though it does
 
2567
        check for divergence to raise an error when the branches are not
 
2568
        either the same, or one a prefix of the other. That behaviour may not
 
2569
        be useful, so that check may be removed in future.
 
2570
 
 
2571
        :param other: The branch to bind to
 
2572
        :type other: Branch
 
2573
        """
 
2574
        # TODO: jam 20051230 Consider checking if the target is bound
 
2575
        #       It is debatable whether you should be able to bind to
 
2576
        #       a branch which is itself bound.
 
2577
        #       Committing is obviously forbidden,
 
2578
        #       but binding itself may not be.
 
2579
        #       Since we *have* to check at commit time, we don't
 
2580
        #       *need* to check here
 
2581
 
 
2582
        # we want to raise diverged if:
 
2583
        # last_rev is not in the other_last_rev history, AND
 
2584
        # other_last_rev is not in our history, and do it without pulling
 
2585
        # history around
 
2586
        self.set_bound_location(other.base)
2625
2587
 
2626
2588
    def get_bound_location(self):
2627
2589
        try:
2669
2631
            return True
2670
2632
 
2671
2633
    @needs_write_lock
2672
 
    def bind(self, other):
2673
 
        """Bind this branch to the branch other.
2674
 
 
2675
 
        This does not push or pull data between the branches, though it does
2676
 
        check for divergence to raise an error when the branches are not
2677
 
        either the same, or one a prefix of the other. That behaviour may not
2678
 
        be useful, so that check may be removed in future.
2679
 
 
2680
 
        :param other: The branch to bind to
2681
 
        :type other: Branch
2682
 
        """
2683
 
        # TODO: jam 20051230 Consider checking if the target is bound
2684
 
        #       It is debatable whether you should be able to bind to
2685
 
        #       a branch which is itself bound.
2686
 
        #       Committing is obviously forbidden,
2687
 
        #       but binding itself may not be.
2688
 
        #       Since we *have* to check at commit time, we don't
2689
 
        #       *need* to check here
2690
 
 
2691
 
        # we want to raise diverged if:
2692
 
        # last_rev is not in the other_last_rev history, AND
2693
 
        # other_last_rev is not in our history, and do it without pulling
2694
 
        # history around
2695
 
        self.set_bound_location(other.base)
2696
 
 
2697
 
    @needs_write_lock
2698
 
    def unbind(self):
2699
 
        """If bound, unbind"""
2700
 
        return self.set_bound_location(None)
2701
 
 
2702
 
    @needs_write_lock
2703
2634
    def update(self, possible_transports=None):
2704
2635
        """Synchronise this branch with the master branch if any.
2705
2636
 
2716
2647
            return old_tip
2717
2648
        return None
2718
2649
 
2719
 
 
2720
 
class BzrBranch8(BzrBranch5):
 
2650
    def _read_last_revision_info(self):
 
2651
        revision_string = self._transport.get_bytes('last-revision')
 
2652
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2653
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2654
        revno = int(revno)
 
2655
        return revno, revision_id
 
2656
 
 
2657
    def _write_last_revision_info(self, revno, revision_id):
 
2658
        """Simply write out the revision id, with no checks.
 
2659
 
 
2660
        Use set_last_revision_info to perform this safely.
 
2661
 
 
2662
        Does not update the revision_history cache.
 
2663
        """
 
2664
        revision_id = _mod_revision.ensure_null(revision_id)
 
2665
        out_string = '%d %s\n' % (revno, revision_id)
 
2666
        self._transport.put_bytes('last-revision', out_string,
 
2667
            mode=self.bzrdir._get_file_mode())
 
2668
 
 
2669
 
 
2670
class FullHistoryBzrBranch(BzrBranch):
 
2671
    """Bzr branch which contains the full revision history."""
 
2672
 
 
2673
    @needs_write_lock
 
2674
    def set_last_revision_info(self, revno, revision_id):
 
2675
        if not revision_id or not isinstance(revision_id, basestring):
 
2676
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2677
        revision_id = _mod_revision.ensure_null(revision_id)
 
2678
        # this old format stores the full history, but this api doesn't
 
2679
        # provide it, so we must generate, and might as well check it's
 
2680
        # correct
 
2681
        history = self._lefthand_history(revision_id)
 
2682
        if len(history) != revno:
 
2683
            raise AssertionError('%d != %d' % (len(history), revno))
 
2684
        self._set_revision_history(history)
 
2685
 
 
2686
    def _read_last_revision_info(self):
 
2687
        rh = self.revision_history()
 
2688
        revno = len(rh)
 
2689
        if revno:
 
2690
            return (revno, rh[-1])
 
2691
        else:
 
2692
            return (0, _mod_revision.NULL_REVISION)
 
2693
 
 
2694
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2695
    @needs_write_lock
 
2696
    def set_revision_history(self, rev_history):
 
2697
        """See Branch.set_revision_history."""
 
2698
        self._set_revision_history(rev_history)
 
2699
 
 
2700
    def _set_revision_history(self, rev_history):
 
2701
        if 'evil' in debug.debug_flags:
 
2702
            mutter_callsite(3, "set_revision_history scales with history.")
 
2703
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2704
        for rev_id in rev_history:
 
2705
            check_not_reserved_id(rev_id)
 
2706
        if Branch.hooks['post_change_branch_tip']:
 
2707
            # Don't calculate the last_revision_info() if there are no hooks
 
2708
            # that will use it.
 
2709
            old_revno, old_revid = self.last_revision_info()
 
2710
        if len(rev_history) == 0:
 
2711
            revid = _mod_revision.NULL_REVISION
 
2712
        else:
 
2713
            revid = rev_history[-1]
 
2714
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2715
        self._write_revision_history(rev_history)
 
2716
        self._clear_cached_state()
 
2717
        self._cache_revision_history(rev_history)
 
2718
        for hook in Branch.hooks['set_rh']:
 
2719
            hook(self, rev_history)
 
2720
        if Branch.hooks['post_change_branch_tip']:
 
2721
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2722
 
 
2723
    def _write_revision_history(self, history):
 
2724
        """Factored out of set_revision_history.
 
2725
 
 
2726
        This performs the actual writing to disk.
 
2727
        It is intended to be called by set_revision_history."""
 
2728
        self._transport.put_bytes(
 
2729
            'revision-history', '\n'.join(history),
 
2730
            mode=self.bzrdir._get_file_mode())
 
2731
 
 
2732
    def _gen_revision_history(self):
 
2733
        history = self._transport.get_bytes('revision-history').split('\n')
 
2734
        if history[-1:] == ['']:
 
2735
            # There shouldn't be a trailing newline, but just in case.
 
2736
            history.pop()
 
2737
        return history
 
2738
 
 
2739
    def _synchronize_history(self, destination, revision_id):
 
2740
        if not isinstance(destination, FullHistoryBzrBranch):
 
2741
            super(BzrBranch, self)._synchronize_history(
 
2742
                destination, revision_id)
 
2743
            return
 
2744
        if revision_id == _mod_revision.NULL_REVISION:
 
2745
            new_history = []
 
2746
        else:
 
2747
            new_history = self.revision_history()
 
2748
        if revision_id is not None and new_history != []:
 
2749
            try:
 
2750
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2751
            except ValueError:
 
2752
                rev = self.repository.get_revision(revision_id)
 
2753
                new_history = rev.get_history(self.repository)[1:]
 
2754
        destination._set_revision_history(new_history)
 
2755
 
 
2756
    @needs_write_lock
 
2757
    def generate_revision_history(self, revision_id, last_rev=None,
 
2758
        other_branch=None):
 
2759
        """Create a new revision history that will finish with revision_id.
 
2760
 
 
2761
        :param revision_id: the new tip to use.
 
2762
        :param last_rev: The previous last_revision. If not None, then this
 
2763
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2764
        :param other_branch: The other branch that DivergedBranches should
 
2765
            raise with respect to.
 
2766
        """
 
2767
        self._set_revision_history(self._lefthand_history(revision_id,
 
2768
            last_rev, other_branch))
 
2769
 
 
2770
 
 
2771
class BzrBranch5(FullHistoryBzrBranch):
 
2772
    """A format 5 branch. This supports new features over plain branches.
 
2773
 
 
2774
    It has support for a master_branch which is the data for bound branches.
 
2775
    """
 
2776
 
 
2777
 
 
2778
class BzrBranch8(BzrBranch):
2721
2779
    """A branch that stores tree-reference locations."""
2722
2780
 
2723
2781
    def _open_hook(self):
2749
2807
        self._last_revision_info_cache = None
2750
2808
        self._reference_info = None
2751
2809
 
2752
 
    def _last_revision_info(self):
2753
 
        revision_string = self._transport.get_bytes('last-revision')
2754
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2755
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2756
 
        revno = int(revno)
2757
 
        return revno, revision_id
2758
 
 
2759
 
    def _write_last_revision_info(self, revno, revision_id):
2760
 
        """Simply write out the revision id, with no checks.
2761
 
 
2762
 
        Use set_last_revision_info to perform this safely.
2763
 
 
2764
 
        Does not update the revision_history cache.
2765
 
        Intended to be called by set_last_revision_info and
2766
 
        _write_revision_history.
2767
 
        """
2768
 
        revision_id = _mod_revision.ensure_null(revision_id)
2769
 
        out_string = '%d %s\n' % (revno, revision_id)
2770
 
        self._transport.put_bytes('last-revision', out_string,
2771
 
            mode=self.bzrdir._get_file_mode())
2772
 
 
2773
 
    @needs_write_lock
2774
 
    def set_last_revision_info(self, revno, revision_id):
2775
 
        if not revision_id or not isinstance(revision_id, basestring):
2776
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2777
 
        old_revno, old_revid = self.last_revision_info()
2778
 
        if self._get_append_revisions_only():
2779
 
            self._check_history_violation(revision_id)
2780
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2781
 
        self._write_last_revision_info(revno, revision_id)
2782
 
        self._clear_cached_state()
2783
 
        self._last_revision_info_cache = revno, revision_id
2784
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2785
 
 
2786
 
    def _synchronize_history(self, destination, revision_id):
2787
 
        """Synchronize last revision and revision history between branches.
2788
 
 
2789
 
        :see: Branch._synchronize_history
2790
 
        """
2791
 
        # XXX: The base Branch has a fast implementation of this method based
2792
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2793
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
2794
 
        # but wants the fast implementation, so it calls
2795
 
        # Branch._synchronize_history directly.
2796
 
        Branch._synchronize_history(self, destination, revision_id)
2797
 
 
2798
2810
    def _check_history_violation(self, revision_id):
2799
2811
        last_revision = _mod_revision.ensure_null(self.last_revision())
2800
2812
        if _mod_revision.is_null(last_revision):
2809
2821
        self._extend_partial_history(stop_index=last_revno-1)
2810
2822
        return list(reversed(self._partial_revision_history_cache))
2811
2823
 
2812
 
    def _write_revision_history(self, history):
2813
 
        """Factored out of set_revision_history.
2814
 
 
2815
 
        This performs the actual writing to disk, with format-specific checks.
2816
 
        It is intended to be called by BzrBranch5.set_revision_history.
2817
 
        """
2818
 
        if len(history) == 0:
2819
 
            last_revision = 'null:'
2820
 
        else:
2821
 
            if history != self._lefthand_history(history[-1]):
2822
 
                raise errors.NotLefthandHistory(history)
2823
 
            last_revision = history[-1]
2824
 
        if self._get_append_revisions_only():
2825
 
            self._check_history_violation(last_revision)
2826
 
        self._write_last_revision_info(len(history), last_revision)
2827
 
 
2828
2824
    @needs_write_lock
2829
2825
    def _set_parent_location(self, url):
2830
2826
        """Set the parent branch"""
2962
2958
        return self.get_config(
2963
2959
            ).get_user_option_as_bool('append_revisions_only')
2964
2960
 
2965
 
    @needs_write_lock
2966
 
    def generate_revision_history(self, revision_id, last_rev=None,
2967
 
                                  other_branch=None):
2968
 
        """See BzrBranch5.generate_revision_history"""
2969
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
2970
 
        revno = len(history)
2971
 
        self.set_last_revision_info(revno, revision_id)
2972
 
 
2973
2961
    @needs_read_lock
2974
2962
    def get_rev_id(self, revno, history=None):
2975
2963
        """Find the revision id of the specified revno."""