~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: John Arbash Meinel
  • Date: 2009-04-30 01:54:04 UTC
  • mfrom: (4312 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4314.
  • Revision ID: john@arbash-meinel.com-20090430015404-iwf322ortskqb1pr
Merge bzr.dev 4312

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
 
18
from cStringIO import StringIO
18
19
import sys
19
20
 
20
21
from bzrlib.lazy_import import lazy_import
30
31
        lockable_files,
31
32
        repository,
32
33
        revision as _mod_revision,
 
34
        rio,
33
35
        symbol_versioning,
34
36
        transport,
35
37
        tsort,
500
502
        """
501
503
        raise errors.UpgradeRequired(self.base)
502
504
 
 
505
    def set_reference_info(self, file_id, tree_path, branch_location):
 
506
        """Set the branch location to use for a tree reference."""
 
507
        raise errors.UnsupportedOperation(self.set_reference_info, self)
 
508
 
 
509
    def get_reference_info(self, file_id):
 
510
        """Get the tree_path and branch_location for a tree reference."""
 
511
        raise errors.UnsupportedOperation(self.get_reference_info, self)
 
512
 
503
513
    @needs_write_lock
504
514
    def fetch(self, from_branch, last_revision=None, pb=None):
505
515
        """Copy revisions from from_branch into this branch.
1070
1080
        revision_id: if not None, the revision history in the new branch will
1071
1081
                     be truncated to end with revision_id.
1072
1082
        """
 
1083
        self.update_references(destination)
1073
1084
        self._synchronize_history(destination, revision_id)
1074
1085
        try:
1075
1086
            parent = self.get_parent()
1081
1092
        if self._push_should_merge_tags():
1082
1093
            self.tags.merge_to(destination.tags)
1083
1094
 
 
1095
    def update_references(self, target):
 
1096
        if not getattr(self._format, 'supports_reference_locations', False):
 
1097
            return
 
1098
        reference_dict = self._get_all_reference_info()
 
1099
        if len(reference_dict) == 0:
 
1100
            return
 
1101
        old_base = self.base
 
1102
        new_base = target.base
 
1103
        target_reference_dict = target._get_all_reference_info()
 
1104
        for file_id, (tree_path, branch_location) in (
 
1105
            reference_dict.items()):
 
1106
            branch_location = urlutils.rebase_url(branch_location,
 
1107
                                                  old_base, new_base)
 
1108
            target_reference_dict.setdefault(
 
1109
                file_id, (tree_path, branch_location))
 
1110
        target._set_all_reference_info(target_reference_dict)
 
1111
 
1084
1112
    @needs_read_lock
1085
1113
    def check(self):
1086
1114
        """Check consistency of the branch.
1134
1162
        return format
1135
1163
 
1136
1164
    def create_clone_on_transport(self, to_transport, revision_id=None,
1137
 
        stacked_on=None):
 
1165
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1138
1166
        """Create a clone of this branch and its bzrdir.
1139
1167
 
1140
1168
        :param to_transport: The transport to clone onto.
1141
1169
        :param revision_id: The revision id to use as tip in the new branch.
1142
1170
            If None the tip is obtained from this branch.
1143
1171
        :param stacked_on: An optional URL to stack the clone on.
 
1172
        :param create_prefix: Create any missing directories leading up to
 
1173
            to_transport.
 
1174
        :param use_existing_dir: Use an existing directory if one exists.
1144
1175
        """
1145
1176
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1146
1177
        # clone call. Or something. 20090224 RBC/spiv.
1147
 
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1148
 
            revision_id=revision_id, stacked_on=stacked_on)
 
1178
        if revision_id is None:
 
1179
            revision_id = self.last_revision()
 
1180
        try:
 
1181
            dir_to = self.bzrdir.clone_on_transport(to_transport,
 
1182
                revision_id=revision_id, stacked_on=stacked_on,
 
1183
                create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1184
        except errors.FileExists:
 
1185
            if not use_existing_dir:
 
1186
                raise
 
1187
        except errors.NoSuchFile:
 
1188
            if not create_prefix:
 
1189
                raise
1149
1190
        return dir_to.open_branch()
1150
1191
 
1151
1192
    def create_checkout(self, to_location, revision_id=None,
1205
1246
        reconciler.reconcile()
1206
1247
        return reconciler
1207
1248
 
1208
 
    def reference_parent(self, file_id, path):
 
1249
    def reference_parent(self, file_id, path, possible_transports=None):
1209
1250
        """Return the parent branch for a tree-reference file_id
1210
1251
        :param file_id: The file_id of the tree reference
1211
1252
        :param path: The path of the file_id in the tree
1212
1253
        :return: A branch associated with the file_id
1213
1254
        """
1214
1255
        # FIXME should provide multiple branches, based on config
1215
 
        return Branch.open(self.bzrdir.root_transport.clone(path).base)
 
1256
        return Branch.open(self.bzrdir.root_transport.clone(path).base,
 
1257
                           possible_transports=possible_transports)
1216
1258
 
1217
1259
    def supports_tags(self):
1218
1260
        return self._format.supports_tags()
1716
1758
 
1717
1759
 
1718
1760
 
1719
 
class BzrBranchFormat7(BranchFormatMetadir):
 
1761
class BzrBranchFormat8(BranchFormatMetadir):
 
1762
    """Metadir format supporting storing locations of subtree branches."""
 
1763
 
 
1764
    def _branch_class(self):
 
1765
        return BzrBranch8
 
1766
 
 
1767
    def get_format_string(self):
 
1768
        """See BranchFormat.get_format_string()."""
 
1769
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
 
1770
 
 
1771
    def get_format_description(self):
 
1772
        """See BranchFormat.get_format_description()."""
 
1773
        return "Branch format 8"
 
1774
 
 
1775
    def initialize(self, a_bzrdir):
 
1776
        """Create a branch of this format in a_bzrdir."""
 
1777
        utf8_files = [('last-revision', '0 null:\n'),
 
1778
                      ('branch.conf', ''),
 
1779
                      ('tags', ''),
 
1780
                      ('references', '')
 
1781
                      ]
 
1782
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1783
 
 
1784
    def __init__(self):
 
1785
        super(BzrBranchFormat8, self).__init__()
 
1786
        self._matchingbzrdir.repository_format = \
 
1787
            RepositoryFormatKnitPack5RichRoot()
 
1788
 
 
1789
    def make_tags(self, branch):
 
1790
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
1791
        return BasicTags(branch)
 
1792
 
 
1793
    def supports_stacking(self):
 
1794
        return True
 
1795
 
 
1796
    supports_reference_locations = True
 
1797
 
 
1798
 
 
1799
class BzrBranchFormat7(BzrBranchFormat8):
1720
1800
    """Branch format with last-revision, tags, and a stacked location pointer.
1721
1801
 
1722
1802
    The stacked location pointer is passed down to the repository and requires
1725
1805
    This format was introduced in bzr 1.6.
1726
1806
    """
1727
1807
 
 
1808
    def initialize(self, a_bzrdir):
 
1809
        """Create a branch of this format in a_bzrdir."""
 
1810
        utf8_files = [('last-revision', '0 null:\n'),
 
1811
                      ('branch.conf', ''),
 
1812
                      ('tags', ''),
 
1813
                      ]
 
1814
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1815
 
1728
1816
    def _branch_class(self):
1729
1817
        return BzrBranch7
1730
1818
 
1736
1824
        """See BranchFormat.get_format_description()."""
1737
1825
        return "Branch format 7"
1738
1826
 
1739
 
    def initialize(self, a_bzrdir):
1740
 
        """Create a branch of this format in a_bzrdir."""
1741
 
        utf8_files = [('last-revision', '0 null:\n'),
1742
 
                      ('branch.conf', ''),
1743
 
                      ('tags', ''),
1744
 
                      ]
1745
 
        return self._initialize_helper(a_bzrdir, utf8_files)
1746
 
 
1747
 
    def __init__(self):
1748
 
        super(BzrBranchFormat7, self).__init__()
1749
 
        self._matchingbzrdir.repository_format = \
1750
 
            RepositoryFormatKnitPack5RichRoot()
1751
 
 
1752
 
    def make_tags(self, branch):
1753
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
1754
 
        return BasicTags(branch)
1755
 
 
1756
 
    def supports_stacking(self):
1757
 
        return True
 
1827
    supports_reference_locations = False
1758
1828
 
1759
1829
 
1760
1830
class BranchReferenceFormat(BranchFormat):
1867
1937
__format5 = BzrBranchFormat5()
1868
1938
__format6 = BzrBranchFormat6()
1869
1939
__format7 = BzrBranchFormat7()
 
1940
__format8 = BzrBranchFormat8()
1870
1941
BranchFormat.register_format(__format5)
1871
1942
BranchFormat.register_format(BranchReferenceFormat())
1872
1943
BranchFormat.register_format(__format6)
1873
1944
BranchFormat.register_format(__format7)
 
1945
BranchFormat.register_format(__format8)
1874
1946
BranchFormat.set_default_format(__format6)
1875
1947
_legacy_formats = [BzrBranchFormat4(),
1876
1948
    ]
2119
2191
        try:
2120
2192
            # We assume that during 'pull' the local repository is closer than
2121
2193
            # the remote one.
 
2194
            source.update_references(self)
2122
2195
            graph = self.repository.get_graph(source.repository)
2123
2196
            result.old_revno, result.old_revid = self.last_revision_info()
2124
2197
            self.update_revisions(source, stop_revision, overwrite=overwrite,
2221
2294
        result.source_branch = self
2222
2295
        result.target_branch = target
2223
2296
        result.old_revno, result.old_revid = target.last_revision_info()
 
2297
        self.update_references(target)
2224
2298
        if result.old_revid != self.last_revision():
2225
2299
            # We assume that during 'push' this repository is closer than
2226
2300
            # the target.
2376
2450
        return None
2377
2451
 
2378
2452
 
2379
 
class BzrBranch7(BzrBranch5):
2380
 
    """A branch with support for a fallback repository."""
 
2453
class BzrBranch8(BzrBranch5):
 
2454
    """A branch that stores tree-reference locations."""
2381
2455
 
2382
2456
    def _open_hook(self):
2383
2457
        if self._ignore_fallbacks:
2399
2473
 
2400
2474
    def __init__(self, *args, **kwargs):
2401
2475
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2402
 
        super(BzrBranch7, self).__init__(*args, **kwargs)
 
2476
        super(BzrBranch8, self).__init__(*args, **kwargs)
2403
2477
        self._last_revision_info_cache = None
2404
2478
        self._partial_revision_history_cache = []
 
2479
        self._reference_info = None
2405
2480
 
2406
2481
    def _clear_cached_state(self):
2407
 
        super(BzrBranch7, self)._clear_cached_state()
 
2482
        super(BzrBranch8, self)._clear_cached_state()
2408
2483
        self._last_revision_info_cache = None
2409
2484
        self._partial_revision_history_cache = []
 
2485
        self._reference_info = None
2410
2486
 
2411
2487
    def _last_revision_info(self):
2412
2488
        revision_string = self._transport.get_bytes('last-revision')
2522
2598
        """Set the parent branch"""
2523
2599
        return self._get_config_location('parent_location')
2524
2600
 
 
2601
    @needs_write_lock
 
2602
    def _set_all_reference_info(self, info_dict):
 
2603
        """Replace all reference info stored in a branch.
 
2604
 
 
2605
        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
 
2606
        """
 
2607
        s = StringIO()
 
2608
        writer = rio.RioWriter(s)
 
2609
        for key, (tree_path, branch_location) in info_dict.iteritems():
 
2610
            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
 
2611
                                branch_location=branch_location)
 
2612
            writer.write_stanza(stanza)
 
2613
        self._transport.put_bytes('references', s.getvalue())
 
2614
        self._reference_info = info_dict
 
2615
 
 
2616
    @needs_read_lock
 
2617
    def _get_all_reference_info(self):
 
2618
        """Return all the reference info stored in a branch.
 
2619
 
 
2620
        :return: A dict of {file_id: (tree_path, branch_location)}
 
2621
        """
 
2622
        if self._reference_info is not None:
 
2623
            return self._reference_info
 
2624
        rio_file = self._transport.get('references')
 
2625
        try:
 
2626
            stanzas = rio.read_stanzas(rio_file)
 
2627
            info_dict = dict((s['file_id'], (s['tree_path'],
 
2628
                             s['branch_location'])) for s in stanzas)
 
2629
        finally:
 
2630
            rio_file.close()
 
2631
        self._reference_info = info_dict
 
2632
        return info_dict
 
2633
 
 
2634
    def set_reference_info(self, file_id, tree_path, branch_location):
 
2635
        """Set the branch location to use for a tree reference.
 
2636
 
 
2637
        :param file_id: The file-id of the tree reference.
 
2638
        :param tree_path: The path of the tree reference in the tree.
 
2639
        :param branch_location: The location of the branch to retrieve tree
 
2640
            references from.
 
2641
        """
 
2642
        info_dict = self._get_all_reference_info()
 
2643
        info_dict[file_id] = (tree_path, branch_location)
 
2644
        if None in (tree_path, branch_location):
 
2645
            if tree_path is not None:
 
2646
                raise ValueError('tree_path must be None when branch_location'
 
2647
                                 ' is None.')
 
2648
            if branch_location is not None:
 
2649
                raise ValueError('branch_location must be None when tree_path'
 
2650
                                 ' is None.')
 
2651
            del info_dict[file_id]
 
2652
        self._set_all_reference_info(info_dict)
 
2653
 
 
2654
    def get_reference_info(self, file_id):
 
2655
        """Get the tree_path and branch_location for a tree reference.
 
2656
 
 
2657
        :return: a tuple of (tree_path, branch_location)
 
2658
        """
 
2659
        return self._get_all_reference_info().get(file_id, (None, None))
 
2660
 
 
2661
    def reference_parent(self, file_id, path, possible_transports=None):
 
2662
        """Return the parent branch for a tree-reference file_id.
 
2663
 
 
2664
        :param file_id: The file_id of the tree reference
 
2665
        :param path: The path of the file_id in the tree
 
2666
        :return: A branch associated with the file_id
 
2667
        """
 
2668
        branch_location = self.get_reference_info(file_id)[1]
 
2669
        if branch_location is None:
 
2670
            return Branch.reference_parent(self, file_id, path,
 
2671
                                           possible_transports)
 
2672
        branch_location = urlutils.join(self.base, branch_location)
 
2673
        return Branch.open(branch_location,
 
2674
                           possible_transports=possible_transports)
 
2675
 
2525
2676
    def set_push_location(self, location):
2526
2677
        """See Branch.set_push_location."""
2527
2678
        self._set_config_location('push_location', location)
2625
2776
        return self.revno() - index
2626
2777
 
2627
2778
 
 
2779
class BzrBranch7(BzrBranch8):
 
2780
    """A branch with support for a fallback repository."""
 
2781
 
 
2782
    def set_reference_info(self, file_id, tree_path, branch_location):
 
2783
        Branch.set_reference_info(self, file_id, tree_path, branch_location)
 
2784
 
 
2785
    def get_reference_info(self, file_id):
 
2786
        Branch.get_reference_info(self, file_id)
 
2787
 
 
2788
    def reference_parent(self, file_id, path, possible_transports=None):
 
2789
        return Branch.reference_parent(self, file_id, path,
 
2790
                                       possible_transports)
 
2791
 
 
2792
 
2628
2793
class BzrBranch6(BzrBranch7):
2629
2794
    """See BzrBranchFormat6 for the capabilities of this branch.
2630
2795
 
2771
2936
        branch._transport.put_bytes('format', format.get_format_string())
2772
2937
 
2773
2938
 
 
2939
class Converter7to8(object):
 
2940
    """Perform an in-place upgrade of format 6 to format 7"""
 
2941
 
 
2942
    def convert(self, branch):
 
2943
        format = BzrBranchFormat8()
 
2944
        branch._transport.put_bytes('references', '')
 
2945
        # update target format
 
2946
        branch._transport.put_bytes('format', format.get_format_string())
 
2947
 
2774
2948
 
2775
2949
def _run_with_write_locked_target(target, callable, *args, **kwargs):
2776
2950
    """Run ``callable(*args, **kwargs)``, write-locking target for the