1930
1888
self.revision_id)
1933
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1934
"""Base class for branch formats that live in meta directories.
1938
BranchFormat.__init__(self)
1939
bzrdir.BzrFormat.__init__(self)
1942
def find_format(klass, controldir, name=None):
1943
"""Return the format for the branch object in controldir."""
1945
transport = controldir.get_branch_transport(None, name=name)
1946
except errors.NoSuchFile:
1947
raise errors.NotBranchError(path=name, bzrdir=controldir)
1949
format_string = transport.get_bytes("format")
1950
except errors.NoSuchFile:
1951
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1952
return klass._find_format(format_registry, 'branch', format_string)
1954
def _branch_class(self):
1955
"""What class to instantiate on open calls."""
1956
raise NotImplementedError(self._branch_class)
1958
def _get_initial_config(self, append_revisions_only=None):
1959
if append_revisions_only:
1960
return "append_revisions_only = True\n"
1962
# Avoid writing anything if append_revisions_only is disabled,
1963
# as that is the default.
1966
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1968
"""Initialize a branch in a control dir, with specified files
1970
:param a_bzrdir: The bzrdir to initialize the branch in
1971
:param utf8_files: The files to create as a list of
1972
(filename, content) tuples
1973
:param name: Name of colocated branch to create, if any
1974
:return: a branch in this format
1977
name = a_bzrdir._get_selected_branch()
1978
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1979
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1980
control_files = lockable_files.LockableFiles(branch_transport,
1981
'lock', lockdir.LockDir)
1982
control_files.create_lock()
1983
control_files.lock_write()
1985
utf8_files += [('format', self.as_string())]
1986
for (filename, content) in utf8_files:
1987
branch_transport.put_bytes(
1989
mode=a_bzrdir._get_file_mode())
1991
control_files.unlock()
1992
branch = self.open(a_bzrdir, name, _found=True,
1993
found_repository=repository)
1994
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1997
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1998
found_repository=None, possible_transports=None):
1999
"""See BranchFormat.open()."""
2001
name = a_bzrdir._get_selected_branch()
2003
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2004
if format.__class__ != self.__class__:
2005
raise AssertionError("wrong format %r found for %r" %
2007
transport = a_bzrdir.get_branch_transport(None, name=name)
2009
control_files = lockable_files.LockableFiles(transport, 'lock',
2011
if found_repository is None:
2012
found_repository = a_bzrdir.find_repository()
2013
return self._branch_class()(_format=self,
2014
_control_files=control_files,
2017
_repository=found_repository,
2018
ignore_fallbacks=ignore_fallbacks,
2019
possible_transports=possible_transports)
2020
except errors.NoSuchFile:
2021
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2024
def _matchingbzrdir(self):
2025
ret = bzrdir.BzrDirMetaFormat1()
2026
ret.set_branch_format(self)
2029
def supports_tags(self):
2032
def supports_leaving_lock(self):
2035
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2037
BranchFormat.check_support_status(self,
2038
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2040
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2041
recommend_upgrade=recommend_upgrade, basedir=basedir)
2044
class BzrBranchFormat6(BranchFormatMetadir):
2045
"""Branch format with last-revision and tags.
2047
Unlike previous formats, this has no explicit revision history. Instead,
2048
this just stores the last-revision, and the left-hand history leading
2049
up to there is the history.
2051
This format was introduced in bzr 0.15
2052
and became the default in 0.91.
2055
def _branch_class(self):
2059
def get_format_string(cls):
2060
"""See BranchFormat.get_format_string()."""
2061
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2063
def get_format_description(self):
2064
"""See BranchFormat.get_format_description()."""
2065
return "Branch format 6"
2067
def initialize(self, a_bzrdir, name=None, repository=None,
2068
append_revisions_only=None):
2069
"""Create a branch of this format in a_bzrdir."""
2070
utf8_files = [('last-revision', '0 null:\n'),
2072
self._get_initial_config(append_revisions_only)),
2075
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2077
def make_tags(self, branch):
2078
"""See breezy.branch.BranchFormat.make_tags()."""
2079
return _mod_tag.BasicTags(branch)
2081
def supports_set_append_revisions_only(self):
2085
class BzrBranchFormat8(BranchFormatMetadir):
2086
"""Metadir format supporting storing locations of subtree branches."""
2088
def _branch_class(self):
2092
def get_format_string(cls):
2093
"""See BranchFormat.get_format_string()."""
2094
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2096
def get_format_description(self):
2097
"""See BranchFormat.get_format_description()."""
2098
return "Branch format 8"
2100
def initialize(self, a_bzrdir, name=None, repository=None,
2101
append_revisions_only=None):
2102
"""Create a branch of this format in a_bzrdir."""
2103
utf8_files = [('last-revision', '0 null:\n'),
2105
self._get_initial_config(append_revisions_only)),
2109
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2111
def make_tags(self, branch):
2112
"""See breezy.branch.BranchFormat.make_tags()."""
2113
return _mod_tag.BasicTags(branch)
2115
def supports_set_append_revisions_only(self):
2118
def supports_stacking(self):
2121
supports_reference_locations = True
2124
class BzrBranchFormat7(BranchFormatMetadir):
2125
"""Branch format with last-revision, tags, and a stacked location pointer.
2127
The stacked location pointer is passed down to the repository and requires
2128
a repository format with supports_external_lookups = True.
2130
This format was introduced in bzr 1.6.
2133
def initialize(self, a_bzrdir, name=None, repository=None,
2134
append_revisions_only=None):
2135
"""Create a branch of this format in a_bzrdir."""
2136
utf8_files = [('last-revision', '0 null:\n'),
2138
self._get_initial_config(append_revisions_only)),
2141
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2143
def _branch_class(self):
2147
def get_format_string(cls):
2148
"""See BranchFormat.get_format_string()."""
2149
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2151
def get_format_description(self):
2152
"""See BranchFormat.get_format_description()."""
2153
return "Branch format 7"
2155
def supports_set_append_revisions_only(self):
2158
def supports_stacking(self):
2161
def make_tags(self, branch):
2162
"""See breezy.branch.BranchFormat.make_tags()."""
2163
return _mod_tag.BasicTags(branch)
2165
supports_reference_locations = False
2168
class BranchReferenceFormat(BranchFormatMetadir):
2169
"""Bzr branch reference format.
2171
Branch references are used in implementing checkouts, they
2172
act as an alias to the real branch which is at some other url.
2180
def get_format_string(cls):
2181
"""See BranchFormat.get_format_string()."""
2182
return "Bazaar-NG Branch Reference Format 1\n"
2184
def get_format_description(self):
2185
"""See BranchFormat.get_format_description()."""
2186
return "Checkout reference format 1"
2188
def get_reference(self, a_bzrdir, name=None):
2189
"""See BranchFormat.get_reference()."""
2190
transport = a_bzrdir.get_branch_transport(None, name=name)
2191
return transport.get_bytes('location')
2193
def set_reference(self, a_bzrdir, name, to_branch):
2194
"""See BranchFormat.set_reference()."""
2195
transport = a_bzrdir.get_branch_transport(None, name=name)
2196
location = transport.put_bytes('location', to_branch.base)
2198
def initialize(self, a_bzrdir, name=None, target_branch=None,
2199
repository=None, append_revisions_only=None):
2200
"""Create a branch of this format in a_bzrdir."""
2201
if target_branch is None:
2202
# this format does not implement branch itself, thus the implicit
2203
# creation contract must see it as uninitializable
2204
raise errors.UninitializableFormat(self)
2205
mutter('creating branch reference in %s', a_bzrdir.user_url)
2206
if a_bzrdir._format.fixed_components:
2207
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2209
name = a_bzrdir._get_selected_branch()
2210
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2211
branch_transport.put_bytes('location',
2212
target_branch.user_url)
2213
branch_transport.put_bytes('format', self.as_string())
2214
branch = self.open(a_bzrdir, name, _found=True,
2215
possible_transports=[target_branch.bzrdir.root_transport])
2216
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2219
def _make_reference_clone_function(format, a_branch):
2220
"""Create a clone() routine for a branch dynamically."""
2221
def clone(to_bzrdir, revision_id=None,
2222
repository_policy=None):
2223
"""See Branch.clone()."""
2224
return format.initialize(to_bzrdir, target_branch=a_branch)
2225
# cannot obey revision_id limits when cloning a reference ...
2226
# FIXME RBC 20060210 either nuke revision_id for clone, or
2227
# emit some sort of warning/error to the caller ?!
2230
def open(self, a_bzrdir, name=None, _found=False, location=None,
2231
possible_transports=None, ignore_fallbacks=False,
2232
found_repository=None):
2233
"""Return the branch that the branch reference in a_bzrdir points at.
2235
:param a_bzrdir: A BzrDir that contains a branch.
2236
:param name: Name of colocated branch to open, if any
2237
:param _found: a private parameter, do not use it. It is used to
2238
indicate if format probing has already be done.
2239
:param ignore_fallbacks: when set, no fallback branches will be opened
2240
(if there are any). Default is to open fallbacks.
2241
:param location: The location of the referenced branch. If
2242
unspecified, this will be determined from the branch reference in
2244
:param possible_transports: An optional reusable transports list.
2247
name = a_bzrdir._get_selected_branch()
2249
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2250
if format.__class__ != self.__class__:
2251
raise AssertionError("wrong format %r found for %r" %
2253
if location is None:
2254
location = self.get_reference(a_bzrdir, name)
2255
real_bzrdir = controldir.ControlDir.open(
2256
location, possible_transports=possible_transports)
2257
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2258
possible_transports=possible_transports)
2259
# this changes the behaviour of result.clone to create a new reference
2260
# rather than a copy of the content of the branch.
2261
# I did not use a proxy object because that needs much more extensive
2262
# testing, and we are only changing one behaviour at the moment.
2263
# If we decide to alter more behaviours - i.e. the implicit nickname
2264
# then this should be refactored to introduce a tested proxy branch
2265
# and a subclass of that for use in overriding clone() and ....
2267
result.clone = self._make_reference_clone_function(result)
2271
1891
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2272
1892
"""Branch format registry."""
2274
1894
def __init__(self, other_registry=None):
2275
1895
super(BranchFormatRegistry, self).__init__(other_registry)
2276
1896
self._default_format = None
1897
self._default_format_key = None
1899
def get_default(self):
1900
"""Return the current default format."""
1901
if (self._default_format_key is not None and
1902
self._default_format is None):
1903
self._default_format = self.get(self._default_format_key)
1904
return self._default_format
2278
1906
def set_default(self, format):
1907
"""Set the default format."""
2279
1908
self._default_format = format
1909
self._default_format_key = None
2281
def get_default(self):
2282
return self._default_format
1911
def set_default_key(self, format_string):
1912
"""Set the default format by its format string."""
1913
self._default_format_key = format_string
1914
self._default_format = None
2285
1917
network_format_registry = registry.FormatRegistry()
2327
class BzrBranch(Branch, _RelockDebugMixin):
2328
"""A branch stored in the actual filesystem.
2330
Note that it's "local" in the context of the filesystem; it doesn't
2331
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2332
it's writable, and can be accessed via the normal filesystem API.
2334
:ivar _transport: Transport for file operations on this branch's
2335
control files, typically pointing to the .bzr/branch directory.
2336
:ivar repository: Repository for this branch.
2337
:ivar base: The url of the base directory for this branch; the one
2338
containing the .bzr directory.
2339
:ivar name: Optional colocated branch name as it exists in the control
2343
def __init__(self, _format=None,
2344
_control_files=None, a_bzrdir=None, name=None,
2345
_repository=None, ignore_fallbacks=False,
2346
possible_transports=None):
2347
"""Create new branch object at a particular location."""
2348
if a_bzrdir is None:
2349
raise ValueError('a_bzrdir must be supplied')
2351
raise ValueError('name must be supplied')
2352
self.bzrdir = a_bzrdir
2353
self._user_transport = self.bzrdir.transport.clone('..')
2355
self._user_transport.set_segment_parameter(
2356
"branch", urlutils.escape(name))
2357
self._base = self._user_transport.base
2359
self._format = _format
2360
if _control_files is None:
2361
raise ValueError('BzrBranch _control_files is None')
2362
self.control_files = _control_files
2363
self._transport = _control_files._transport
2364
self.repository = _repository
2365
self.conf_store = None
2366
Branch.__init__(self, possible_transports)
2369
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2373
def _get_base(self):
2374
"""Returns the directory containing the control directory."""
2377
base = property(_get_base, doc="The URL for the root of this branch.")
2380
def user_transport(self):
2381
return self._user_transport
2383
def _get_config(self):
2384
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2386
def _get_config_store(self):
2387
if self.conf_store is None:
2388
self.conf_store = _mod_config.BranchStore(self)
2389
return self.conf_store
2391
def _uncommitted_branch(self):
2392
"""Return the branch that may contain uncommitted changes."""
2393
master = self.get_master_branch()
2394
if master is not None:
2399
def store_uncommitted(self, creator):
2400
"""Store uncommitted changes from a ShelfCreator.
2402
:param creator: The ShelfCreator containing uncommitted changes, or
2403
None to delete any stored changes.
2404
:raises: ChangesAlreadyStored if the branch already has changes.
2406
branch = self._uncommitted_branch()
2408
branch._transport.delete('stored-transform')
2410
if branch._transport.has('stored-transform'):
2411
raise errors.ChangesAlreadyStored
2412
transform = BytesIO()
2413
creator.write_shelf(transform)
2415
branch._transport.put_file('stored-transform', transform)
2417
def get_unshelver(self, tree):
2418
"""Return a shelf.Unshelver for this branch and tree.
2420
:param tree: The tree to use to construct the Unshelver.
2421
:return: an Unshelver or None if no changes are stored.
2423
branch = self._uncommitted_branch()
2425
transform = branch._transport.get('stored-transform')
2426
except errors.NoSuchFile:
2428
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2430
def is_locked(self):
2431
return self.control_files.is_locked()
2433
def lock_write(self, token=None):
2434
"""Lock the branch for write operations.
2436
:param token: A token to permit reacquiring a previously held and
2438
:return: A BranchWriteLockResult.
2440
if not self.is_locked():
2441
self._note_lock('w')
2442
self.repository._warn_if_deprecated(self)
2443
self.repository.lock_write()
2448
return BranchWriteLockResult(self.unlock,
2449
self.control_files.lock_write(token=token))
2452
self.repository.unlock()
2455
def lock_read(self):
2456
"""Lock the branch for read operations.
2458
:return: A breezy.lock.LogicalLockResult.
2460
if not self.is_locked():
2461
self._note_lock('r')
2462
self.repository._warn_if_deprecated(self)
2463
self.repository.lock_read()
2468
self.control_files.lock_read()
2469
return LogicalLockResult(self.unlock)
2472
self.repository.unlock()
2475
@only_raises(errors.LockNotHeld, errors.LockBroken)
2477
if self.control_files._lock_count == 1 and self.conf_store is not None:
2478
self.conf_store.save_changes()
2480
self.control_files.unlock()
2482
if not self.control_files.is_locked():
2483
self.repository.unlock()
2484
# we just released the lock
2485
self._clear_cached_state()
2487
def peek_lock_mode(self):
2488
if self.control_files._lock_count == 0:
2491
return self.control_files._lock_mode
2493
def get_physical_lock_status(self):
2494
return self.control_files.get_physical_lock_status()
2497
def print_file(self, file, revision_id):
2498
"""See Branch.print_file."""
2499
return self.repository.print_file(file, revision_id)
2502
def set_last_revision_info(self, revno, revision_id):
2503
if not revision_id or not isinstance(revision_id, basestring):
2504
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2505
revision_id = _mod_revision.ensure_null(revision_id)
2506
old_revno, old_revid = self.last_revision_info()
2507
if self.get_append_revisions_only():
2508
self._check_history_violation(revision_id)
2509
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2510
self._write_last_revision_info(revno, revision_id)
2511
self._clear_cached_state()
2512
self._last_revision_info_cache = revno, revision_id
2513
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2515
def basis_tree(self):
2516
"""See Branch.basis_tree."""
2517
return self.repository.revision_tree(self.last_revision())
2519
def _get_parent_location(self):
2520
_locs = ['parent', 'pull', 'x-pull']
2523
return self._transport.get_bytes(l).strip('\n')
2524
except errors.NoSuchFile:
2528
def get_stacked_on_url(self):
2529
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2531
def set_push_location(self, location):
2532
"""See Branch.set_push_location."""
2533
self.get_config().set_user_option(
2534
'push_location', location,
2535
store=_mod_config.STORE_LOCATION_NORECURSE)
2537
def _set_parent_location(self, url):
2539
self._transport.delete('parent')
2541
self._transport.put_bytes('parent', url + '\n',
2542
mode=self.bzrdir._get_file_mode())
2546
"""If bound, unbind"""
2547
return self.set_bound_location(None)
2550
def bind(self, other):
2551
"""Bind this branch to the branch other.
2553
This does not push or pull data between the branches, though it does
2554
check for divergence to raise an error when the branches are not
2555
either the same, or one a prefix of the other. That behaviour may not
2556
be useful, so that check may be removed in future.
2558
:param other: The branch to bind to
2561
# TODO: jam 20051230 Consider checking if the target is bound
2562
# It is debatable whether you should be able to bind to
2563
# a branch which is itself bound.
2564
# Committing is obviously forbidden,
2565
# but binding itself may not be.
2566
# Since we *have* to check at commit time, we don't
2567
# *need* to check here
2569
# we want to raise diverged if:
2570
# last_rev is not in the other_last_rev history, AND
2571
# other_last_rev is not in our history, and do it without pulling
2573
self.set_bound_location(other.base)
2575
def get_bound_location(self):
2577
return self._transport.get_bytes('bound')[:-1]
2578
except errors.NoSuchFile:
2582
def get_master_branch(self, possible_transports=None):
2583
"""Return the branch we are bound to.
2585
:return: Either a Branch, or None
2587
if self._master_branch_cache is None:
2588
self._master_branch_cache = self._get_master_branch(
2589
possible_transports)
2590
return self._master_branch_cache
2592
def _get_master_branch(self, possible_transports):
2593
bound_loc = self.get_bound_location()
2597
return Branch.open(bound_loc,
2598
possible_transports=possible_transports)
2599
except (errors.NotBranchError, errors.ConnectionError) as e:
2600
raise errors.BoundBranchConnectionFailure(
2604
def set_bound_location(self, location):
2605
"""Set the target where this branch is bound to.
2607
:param location: URL to the target branch
2609
self._master_branch_cache = None
2611
self._transport.put_bytes('bound', location+'\n',
2612
mode=self.bzrdir._get_file_mode())
2615
self._transport.delete('bound')
2616
except errors.NoSuchFile:
2621
def update(self, possible_transports=None):
2622
"""Synchronise this branch with the master branch if any.
2624
:return: None or the last_revision that was pivoted out during the
2627
master = self.get_master_branch(possible_transports)
2628
if master is not None:
2629
old_tip = _mod_revision.ensure_null(self.last_revision())
2630
self.pull(master, overwrite=True)
2631
if self.repository.get_graph().is_ancestor(old_tip,
2632
_mod_revision.ensure_null(self.last_revision())):
2637
def _read_last_revision_info(self):
2638
revision_string = self._transport.get_bytes('last-revision')
2639
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2640
revision_id = cache_utf8.get_cached_utf8(revision_id)
2642
return revno, revision_id
2644
def _write_last_revision_info(self, revno, revision_id):
2645
"""Simply write out the revision id, with no checks.
2647
Use set_last_revision_info to perform this safely.
2649
Does not update the revision_history cache.
2651
revision_id = _mod_revision.ensure_null(revision_id)
2652
out_string = '%d %s\n' % (revno, revision_id)
2653
self._transport.put_bytes('last-revision', out_string,
2654
mode=self.bzrdir._get_file_mode())
2657
def update_feature_flags(self, updated_flags):
2658
"""Update the feature flags for this branch.
2660
:param updated_flags: Dictionary mapping feature names to necessities
2661
A necessity can be None to indicate the feature should be removed
2663
self._format._update_feature_flags(updated_flags)
2664
self.control_transport.put_bytes('format', self._format.as_string())
2667
class BzrBranch8(BzrBranch):
2668
"""A branch that stores tree-reference locations."""
2670
def _open_hook(self, possible_transports=None):
2671
if self._ignore_fallbacks:
2673
if possible_transports is None:
2674
possible_transports = [self.bzrdir.root_transport]
2676
url = self.get_stacked_on_url()
2677
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2678
errors.UnstackableBranchFormat):
2681
for hook in Branch.hooks['transform_fallback_location']:
2682
url = hook(self, url)
2684
hook_name = Branch.hooks.get_hook_name(hook)
2685
raise AssertionError(
2686
"'transform_fallback_location' hook %s returned "
2687
"None, not a URL." % hook_name)
2688
self._activate_fallback_location(url,
2689
possible_transports=possible_transports)
2691
def __init__(self, *args, **kwargs):
2692
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2693
super(BzrBranch8, self).__init__(*args, **kwargs)
2694
self._last_revision_info_cache = None
2695
self._reference_info = None
2697
def _clear_cached_state(self):
2698
super(BzrBranch8, self)._clear_cached_state()
2699
self._last_revision_info_cache = None
2700
self._reference_info = None
2702
def _check_history_violation(self, revision_id):
2703
current_revid = self.last_revision()
2704
last_revision = _mod_revision.ensure_null(current_revid)
2705
if _mod_revision.is_null(last_revision):
2707
graph = self.repository.get_graph()
2708
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2709
if lh_ancestor == current_revid:
2711
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2713
def _gen_revision_history(self):
2714
"""Generate the revision history from last revision
2716
last_revno, last_revision = self.last_revision_info()
2717
self._extend_partial_history(stop_index=last_revno-1)
2718
return list(reversed(self._partial_revision_history_cache))
2721
def _set_parent_location(self, url):
2722
"""Set the parent branch"""
2723
self._set_config_location('parent_location', url, make_relative=True)
2726
def _get_parent_location(self):
2727
"""Set the parent branch"""
2728
return self._get_config_location('parent_location')
2731
def _set_all_reference_info(self, info_dict):
2732
"""Replace all reference info stored in a branch.
2734
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2737
writer = rio.RioWriter(s)
2738
for key, (tree_path, branch_location) in viewitems(info_dict):
2739
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2740
branch_location=branch_location)
2741
writer.write_stanza(stanza)
2742
self._transport.put_bytes('references', s.getvalue())
2743
self._reference_info = info_dict
2746
def _get_all_reference_info(self):
2747
"""Return all the reference info stored in a branch.
2749
:return: A dict of {file_id: (tree_path, branch_location)}
2751
if self._reference_info is not None:
2752
return self._reference_info
2753
rio_file = self._transport.get('references')
2755
stanzas = rio.read_stanzas(rio_file)
2756
info_dict = dict((s['file_id'], (s['tree_path'],
2757
s['branch_location'])) for s in stanzas)
2760
self._reference_info = info_dict
2763
def set_reference_info(self, file_id, tree_path, branch_location):
2764
"""Set the branch location to use for a tree reference.
2766
:param file_id: The file-id of the tree reference.
2767
:param tree_path: The path of the tree reference in the tree.
2768
:param branch_location: The location of the branch to retrieve tree
2771
info_dict = self._get_all_reference_info()
2772
info_dict[file_id] = (tree_path, branch_location)
2773
if None in (tree_path, branch_location):
2774
if tree_path is not None:
2775
raise ValueError('tree_path must be None when branch_location'
2777
if branch_location is not None:
2778
raise ValueError('branch_location must be None when tree_path'
2780
del info_dict[file_id]
2781
self._set_all_reference_info(info_dict)
2783
def get_reference_info(self, file_id):
2784
"""Get the tree_path and branch_location for a tree reference.
2786
:return: a tuple of (tree_path, branch_location)
2788
return self._get_all_reference_info().get(file_id, (None, None))
2790
def reference_parent(self, file_id, path, possible_transports=None):
2791
"""Return the parent branch for a tree-reference file_id.
2793
:param file_id: The file_id of the tree reference
2794
:param path: The path of the file_id in the tree
2795
:return: A branch associated with the file_id
2797
branch_location = self.get_reference_info(file_id)[1]
2798
if branch_location is None:
2799
return Branch.reference_parent(self, file_id, path,
2800
possible_transports)
2801
branch_location = urlutils.join(self.user_url, branch_location)
2802
return Branch.open(branch_location,
2803
possible_transports=possible_transports)
2805
def set_push_location(self, location):
2806
"""See Branch.set_push_location."""
2807
self._set_config_location('push_location', location)
2809
def set_bound_location(self, location):
2810
"""See Branch.set_push_location."""
2811
self._master_branch_cache = None
2813
conf = self.get_config_stack()
2814
if location is None:
2815
if not conf.get('bound'):
2818
conf.set('bound', 'False')
2821
self._set_config_location('bound_location', location,
2823
conf.set('bound', 'True')
2826
def _get_bound_location(self, bound):
2827
"""Return the bound location in the config file.
2829
Return None if the bound parameter does not match"""
2830
conf = self.get_config_stack()
2831
if conf.get('bound') != bound:
2833
return self._get_config_location('bound_location', config=conf)
2835
def get_bound_location(self):
2836
"""See Branch.get_bound_location."""
2837
return self._get_bound_location(True)
2839
def get_old_bound_location(self):
2840
"""See Branch.get_old_bound_location"""
2841
return self._get_bound_location(False)
2843
def get_stacked_on_url(self):
2844
# you can always ask for the URL; but you might not be able to use it
2845
# if the repo can't support stacking.
2846
## self._check_stackable_repo()
2847
# stacked_on_location is only ever defined in branch.conf, so don't
2848
# waste effort reading the whole stack of config files.
2849
conf = _mod_config.BranchOnlyStack(self)
2850
stacked_url = self._get_config_location('stacked_on_location',
2852
if stacked_url is None:
2853
raise errors.NotStacked(self)
2854
return stacked_url.encode('utf-8')
2857
def get_rev_id(self, revno, history=None):
2858
"""Find the revision id of the specified revno."""
2860
return _mod_revision.NULL_REVISION
2862
last_revno, last_revision_id = self.last_revision_info()
2863
if revno <= 0 or revno > last_revno:
2864
raise errors.NoSuchRevision(self, revno)
2866
if history is not None:
2867
return history[revno - 1]
2869
index = last_revno - revno
2870
if len(self._partial_revision_history_cache) <= index:
2871
self._extend_partial_history(stop_index=index)
2872
if len(self._partial_revision_history_cache) > index:
2873
return self._partial_revision_history_cache[index]
2875
raise errors.NoSuchRevision(self, revno)
2878
def revision_id_to_revno(self, revision_id):
2879
"""Given a revision id, return its revno"""
2880
if _mod_revision.is_null(revision_id):
2883
index = self._partial_revision_history_cache.index(revision_id)
2886
self._extend_partial_history(stop_revision=revision_id)
2887
except errors.RevisionNotPresent as e:
2888
raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2889
index = len(self._partial_revision_history_cache) - 1
2891
raise errors.NoSuchRevision(self, revision_id)
2892
if self._partial_revision_history_cache[index] != revision_id:
2893
raise errors.NoSuchRevision(self, revision_id)
2894
return self.revno() - index
2897
class BzrBranch7(BzrBranch8):
2898
"""A branch with support for a fallback repository."""
2900
def set_reference_info(self, file_id, tree_path, branch_location):
2901
Branch.set_reference_info(self, file_id, tree_path, branch_location)
2903
def get_reference_info(self, file_id):
2904
Branch.get_reference_info(self, file_id)
2906
def reference_parent(self, file_id, path, possible_transports=None):
2907
return Branch.reference_parent(self, file_id, path,
2908
possible_transports)
2911
class BzrBranch6(BzrBranch7):
2912
"""See BzrBranchFormat6 for the capabilities of this branch.
2914
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2918
def get_stacked_on_url(self):
2919
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2922
1966
######################################################################
2923
1967
# results of operations