1
# Copyright (C) 2005-2012 Canonical Ltd
1
# Copyright (C) 2005-2011 Canonical Ltd
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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
17
import bzrlib.bzrdir
21
19
from cStringIO import StringIO
48
45
from bzrlib.i18n import gettext, ngettext
51
# Explicitly import bzrlib.bzrdir so that the BzrProber
52
# is guaranteed to be registered.
55
48
from bzrlib import (
59
51
from bzrlib.decorators import (
94
86
def user_transport(self):
95
87
return self.bzrdir.user_transport
97
def __init__(self, possible_transports=None):
89
def __init__(self, *ignored, **ignored_too):
98
90
self.tags = self._format.make_tags(self)
99
91
self._revision_history_cache = None
100
92
self._revision_id_to_revno_cache = None
104
96
self._last_revision_info_cache = None
105
97
self._master_branch_cache = None
106
98
self._merge_sorted_revisions_cache = None
107
self._open_hook(possible_transports)
108
100
hooks = Branch.hooks['open']
109
101
for hook in hooks:
112
def _open_hook(self, possible_transports):
104
def _open_hook(self):
113
105
"""Called by init to allow simpler extension of the base class."""
115
def _activate_fallback_location(self, url, possible_transports):
107
def _activate_fallback_location(self, url):
116
108
"""Activate the branch/repository from url as a fallback repository."""
117
109
for existing_fallback_repo in self.repository._fallback_repositories:
118
110
if existing_fallback_repo.user_url == url:
121
113
# confusing _unstack we don't add this a second time.
122
114
mutter('duplicate activation of fallback %r on %r', url, self)
124
repo = self._get_fallback_repository(url, possible_transports)
116
repo = self._get_fallback_repository(url)
125
117
if repo.has_same_location(self.repository):
126
118
raise errors.UnstackableLocationError(self.user_url, url)
127
119
self.repository.add_fallback_repository(repo)
181
173
For instance, if the branch is at URL/.bzr/branch,
182
174
Branch.open(URL) -> a Branch instance.
184
control = controldir.ControlDir.open(base,
185
possible_transports=possible_transports, _unsupported=_unsupported)
186
return control.open_branch(unsupported=_unsupported,
187
possible_transports=possible_transports)
176
control = controldir.ControlDir.open(base, _unsupported,
177
possible_transports=possible_transports)
178
return control.open_branch(unsupported=_unsupported)
190
def open_from_transport(transport, name=None, _unsupported=False,
191
possible_transports=None):
181
def open_from_transport(transport, name=None, _unsupported=False):
192
182
"""Open the branch rooted at transport"""
193
183
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
194
return control.open_branch(name=name, unsupported=_unsupported,
195
possible_transports=possible_transports)
184
return control.open_branch(name=name, unsupported=_unsupported)
198
187
def open_containing(url, possible_transports=None):
209
198
control, relpath = controldir.ControlDir.open_containing(url,
210
199
possible_transports)
211
branch = control.open_branch(possible_transports=possible_transports)
212
return (branch, relpath)
200
return control.open_branch(), relpath
214
202
def _push_should_merge_tags(self):
215
203
"""Should _basic_push merge this branch's tags into the target?
252
240
raise NotImplementedError(self._get_config)
254
def _get_fallback_repository(self, url, possible_transports):
242
def _get_fallback_repository(self, url):
255
243
"""Get the repository we fallback to at url."""
256
244
url = urlutils.join(self.base, url)
257
a_branch = Branch.open(url, possible_transports=possible_transports)
245
a_branch = Branch.open(url,
246
possible_transports=[self.bzrdir.root_transport])
258
247
return a_branch.repository
671
660
if not self._format.supports_set_append_revisions_only():
673
return self.get_config_stack().get('append_revisions_only')
662
return self.get_config(
663
).get_user_option_as_bool('append_revisions_only')
675
665
def set_append_revisions_only(self, enabled):
676
666
if not self._format.supports_set_append_revisions_only():
677
667
raise errors.UpgradeRequired(self.user_url)
678
self.get_config_stack().set('append_revisions_only', enabled)
672
self.get_config().set_user_option('append_revisions_only', value,
680
675
def set_reference_info(self, file_id, tree_path, branch_location):
681
676
"""Set the branch location to use for a tree reference."""
711
706
raise errors.UpgradeRequired(self.user_url)
713
def get_commit_builder(self, parents, config_stack=None, timestamp=None,
708
def get_commit_builder(self, parents, config=None, timestamp=None,
714
709
timezone=None, committer=None, revprops=None,
715
710
revision_id=None, lossy=False):
716
711
"""Obtain a CommitBuilder for this branch.
726
721
represented, when pushing to a foreign VCS
729
if config_stack is None:
730
config_stack = self.get_config_stack()
725
config = self.get_config()
732
return self.repository.get_commit_builder(self, parents, config_stack,
727
return self.repository.get_commit_builder(self, parents, config,
733
728
timestamp, timezone, committer, revprops, revision_id,
859
self._activate_fallback_location(url,
860
possible_transports=[self.bzrdir.root_transport])
854
self._activate_fallback_location(url)
861
855
# write this out after the repository is stacked to avoid setting a
862
856
# stacked config that doesn't work.
863
857
self._set_config_location('stacked_on_location', url)
939
933
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
940
934
except errors.TagsNotSupported:
941
935
tags_to_fetch = set()
942
fetch_spec = vf_search.NotInOtherForRevs(self.repository,
936
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
943
937
old_repository, required_ids=[self.last_revision()],
944
938
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
945
939
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
1170
1164
def _set_config_location(self, name, url, config=None,
1171
1165
make_relative=False):
1172
1166
if config is None:
1173
config = self.get_config_stack()
1167
config = self.get_config()
1174
1168
if url is None:
1176
1170
elif make_relative:
1177
1171
url = urlutils.relative_url(self.base, url)
1178
config.set(name, url)
1172
config.set_user_option(name, url, warn_masked=True)
1180
1174
def _get_config_location(self, name, config=None):
1181
1175
if config is None:
1182
config = self.get_config_stack()
1183
location = config.get(name)
1176
config = self.get_config()
1177
location = config.get_user_option(name)
1184
1178
if location == '':
1185
1179
location = None
1186
1180
return location
1188
1182
def get_child_submit_format(self):
1189
1183
"""Return the preferred format of submissions to this branch."""
1190
return self.get_config_stack().get('child_submit_format')
1184
return self.get_config().get_user_option("child_submit_format")
1192
1186
def get_submit_branch(self):
1193
1187
"""Return the submit location of the branch.
1196
1190
pattern is that the user can override it by specifying a
1199
return self.get_config_stack().get('submit_branch')
1193
return self.get_config().get_user_option('submit_branch')
1201
1195
def set_submit_branch(self, location):
1202
1196
"""Return the submit location of the branch.
1205
1199
pattern is that the user can override it by specifying a
1208
self.get_config_stack().set('submit_branch', location)
1202
self.get_config().set_user_option('submit_branch', location,
1210
1205
def get_public_branch(self):
1211
1206
"""Return the public location of the branch.
1224
1219
self._set_config_location('public_branch', location)
1226
1221
def get_push_location(self):
1227
"""Return None or the location to push this branch to."""
1228
return self.get_config_stack().get('push_location')
1222
"""Return the None or the location to push this branch to."""
1223
push_loc = self.get_config().get_user_option('push_location')
1230
1226
def set_push_location(self, location):
1231
1227
"""Set a new push location for this branch."""
1457
1453
t = transport.get_transport(to_location)
1458
1454
t.ensure_base()
1459
1455
format = self._get_checkout_format(lightweight=lightweight)
1461
1457
checkout = format.initialize_on_transport(t)
1462
except errors.AlreadyControlDirError:
1463
# It's fine if the control directory already exists,
1464
# as long as there is no existing branch and working tree.
1465
checkout = controldir.ControlDir.open_from_transport(t)
1467
checkout.open_branch()
1468
except errors.NotBranchError:
1471
raise errors.AlreadyControlDirError(t.base)
1472
if checkout.control_transport.base == self.bzrdir.control_transport.base:
1473
# When checking out to the same control directory,
1474
# always create a lightweight checkout
1478
from_branch = checkout.set_branch_reference(target_branch=self)
1458
from_branch = BranchReferenceFormat().initialize(checkout,
1480
policy = checkout.determine_repository_policy()
1481
repo = policy.acquire_repository()[0]
1482
checkout_branch = checkout.create_branch()
1461
checkout_branch = controldir.ControlDir.create_branch_convenience(
1462
to_location, force_new_tree=False, format=format)
1463
checkout = checkout_branch.bzrdir
1483
1464
checkout_branch.bind(self)
1484
1465
# pull up to the specified revision_id to set the initial
1485
1466
# branch tip correctly, and seed it with history.
1486
1467
checkout_branch.pull(self, stop_revision=revision_id)
1488
1469
tree = checkout.create_workingtree(revision_id,
1489
1470
from_branch=from_branch,
1490
1471
accelerator_tree=accelerator_tree,
1579
1560
heads that must be fetched if present, but no error is necessary if
1580
1561
they are not present.
1582
# For bzr native formats must_fetch is just the tip, and
1583
# if_present_fetch are the tags.
1563
# For bzr native formats must_fetch is just the tip, and if_present_fetch
1584
1565
must_fetch = set([self.last_revision()])
1585
1566
if_present_fetch = set()
1586
if self.get_config_stack().get('branch.fetch_tags'):
1567
c = self.get_config()
1568
include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1588
1572
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1589
1573
except errors.TagsNotSupported:
1618
1602
return not (self == other)
1605
def find_format(klass, controldir, name=None):
1606
"""Return the format for the branch object in controldir."""
1608
transport = controldir.get_branch_transport(None, name=name)
1609
format_string = transport.get_bytes("format")
1610
return format_registry.get(format_string)
1611
except errors.NoSuchFile:
1612
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1614
raise errors.UnknownFormatError(format=format_string, kind='branch')
1621
1617
@deprecated_method(deprecated_in((2, 4, 0)))
1622
1618
def get_default_format(klass):
1623
1619
"""Return the current default format."""
1661
1657
raise NotImplementedError(self.set_reference)
1659
def get_format_string(self):
1660
"""Return the ASCII format string that identifies this format."""
1661
raise NotImplementedError(self.get_format_string)
1663
1663
def get_format_description(self):
1664
1664
"""Return the short format description for this format."""
1665
1665
raise NotImplementedError(self.get_format_description)
1714
1714
raise NotImplementedError(self.network_name)
1716
1716
def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1717
found_repository=None, possible_transports=None):
1717
found_repository=None):
1718
1718
"""Return the branch object for controldir.
1720
1720
:param controldir: A ControlDir that contains a branch.
1793
1793
registry._LazyObjectGetter.__init__(self, module_name, member_name)
1794
1794
self._format_string = format_string
1796
1796
def get_format_string(self):
1797
1797
"""See BranchFormat.get_format_string."""
1798
1798
return self._format_string
2010
2010
self.revision_id)
2013
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
2014
"""Base class for branch formats that live in meta directories.
2018
BranchFormat.__init__(self)
2019
bzrdir.BzrFormat.__init__(self)
2022
def find_format(klass, controldir, name=None):
2023
"""Return the format for the branch object in controldir."""
2025
transport = controldir.get_branch_transport(None, name=name)
2026
except errors.NoSuchFile:
2027
raise errors.NotBranchError(path=name, bzrdir=controldir)
2029
format_string = transport.get_bytes("format")
2030
except errors.NoSuchFile:
2031
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2032
return klass._find_format(format_registry, 'branch', format_string)
2013
class BranchFormatMetadir(BranchFormat):
2014
"""Common logic for meta-dir based branch formats."""
2034
2016
def _branch_class(self):
2035
2017
"""What class to instantiate on open calls."""
2053
2035
:param name: Name of colocated branch to create, if any
2054
2036
:return: a branch in this format
2057
name = a_bzrdir._get_selected_branch()
2058
2038
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2059
2039
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2060
2040
control_files = lockable_files.LockableFiles(branch_transport,
2062
2042
control_files.create_lock()
2063
2043
control_files.lock_write()
2065
utf8_files += [('format', self.as_string())]
2045
utf8_files += [('format', self.get_format_string())]
2066
2046
for (filename, content) in utf8_files:
2067
2047
branch_transport.put_bytes(
2068
2048
filename, content,
2074
2054
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2057
def network_name(self):
2058
"""A simple byte string uniquely identifying this format for RPC calls.
2060
Metadir branch formats use their format string.
2062
return self.get_format_string()
2077
2064
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2078
found_repository=None, possible_transports=None):
2065
found_repository=None):
2079
2066
"""See BranchFormat.open()."""
2081
name = a_bzrdir._get_selected_branch()
2083
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2068
format = BranchFormat.find_format(a_bzrdir, name=name)
2084
2069
if format.__class__ != self.__class__:
2085
2070
raise AssertionError("wrong format %r found for %r" %
2086
2071
(format, self))
2096
2081
a_bzrdir=a_bzrdir,
2097
2082
_repository=found_repository,
2098
ignore_fallbacks=ignore_fallbacks,
2099
possible_transports=possible_transports)
2083
ignore_fallbacks=ignore_fallbacks)
2100
2084
except errors.NoSuchFile:
2101
2085
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2112
2096
def supports_leaving_lock(self):
2115
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2117
BranchFormat.check_support_status(self,
2118
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2120
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2121
recommend_upgrade=recommend_upgrade, basedir=basedir)
2124
2100
class BzrBranchFormat5(BranchFormatMetadir):
2125
2101
"""Bzr branch format 5.
2324
2295
mutter('creating branch reference in %s', a_bzrdir.user_url)
2325
2296
if a_bzrdir._format.fixed_components:
2326
2297
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2328
name = a_bzrdir._get_selected_branch()
2329
2298
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2330
2299
branch_transport.put_bytes('location',
2331
target_branch.user_url)
2332
branch_transport.put_bytes('format', self.as_string())
2333
branch = self.open(a_bzrdir, name, _found=True,
2300
target_branch.bzrdir.user_url)
2301
branch_transport.put_bytes('format', self.get_format_string())
2303
a_bzrdir, name, _found=True,
2334
2304
possible_transports=[target_branch.bzrdir.root_transport])
2335
2305
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2363
2333
:param possible_transports: An optional reusable transports list.
2366
name = a_bzrdir._get_selected_branch()
2368
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2336
format = BranchFormat.find_format(a_bzrdir, name=name)
2369
2337
if format.__class__ != self.__class__:
2370
2338
raise AssertionError("wrong format %r found for %r" %
2371
2339
(format, self))
2373
2341
location = self.get_reference(a_bzrdir, name)
2374
2342
real_bzrdir = controldir.ControlDir.open(
2375
2343
location, possible_transports=possible_transports)
2376
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2377
possible_transports=possible_transports)
2344
result = real_bzrdir.open_branch(name=name,
2345
ignore_fallbacks=ignore_fallbacks)
2378
2346
# this changes the behaviour of result.clone to create a new reference
2379
2347
# rather than a copy of the content of the branch.
2380
2348
# I did not use a proxy object because that needs much more extensive
2462
2430
def __init__(self, _format=None,
2463
2431
_control_files=None, a_bzrdir=None, name=None,
2464
_repository=None, ignore_fallbacks=False,
2465
possible_transports=None):
2432
_repository=None, ignore_fallbacks=False):
2466
2433
"""Create new branch object at a particular location."""
2467
2434
if a_bzrdir is None:
2468
2435
raise ValueError('a_bzrdir must be supplied')
2470
raise ValueError('name must be supplied')
2471
self.bzrdir = a_bzrdir
2472
self._user_transport = self.bzrdir.transport.clone('..')
2474
self._user_transport.set_segment_parameter(
2475
"branch", urlutils.escape(name))
2476
self._base = self._user_transport.base
2437
self.bzrdir = a_bzrdir
2438
self._base = self.bzrdir.transport.clone('..').base
2477
2439
self.name = name
2440
# XXX: We should be able to just do
2441
# self.base = self.bzrdir.root_transport.base
2442
# but this does not quite work yet -- mbp 20080522
2478
2443
self._format = _format
2479
2444
if _control_files is None:
2480
2445
raise ValueError('BzrBranch _control_files is None')
2481
2446
self.control_files = _control_files
2482
2447
self._transport = _control_files._transport
2483
2448
self.repository = _repository
2484
self.conf_store = None
2485
Branch.__init__(self, possible_transports)
2449
Branch.__init__(self)
2487
2451
def __str__(self):
2488
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2452
if self.name is None:
2453
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2455
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2490
2458
__repr__ = __str__
2496
2464
base = property(_get_base, doc="The URL for the root of this branch.")
2499
def user_transport(self):
2500
return self._user_transport
2502
2466
def _get_config(self):
2503
2467
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2505
def _get_config_store(self):
2506
if self.conf_store is None:
2507
self.conf_store = _mod_config.BranchStore(self)
2508
return self.conf_store
2510
2469
def is_locked(self):
2511
2470
return self.control_files.is_locked()
2561
2520
@only_raises(errors.LockNotHeld, errors.LockBroken)
2562
2521
def unlock(self):
2563
if self.conf_store is not None:
2564
self.conf_store.save_changes()
2566
2523
self.control_files.unlock()
2743
2700
self._transport.put_bytes('last-revision', out_string,
2744
2701
mode=self.bzrdir._get_file_mode())
2747
def update_feature_flags(self, updated_flags):
2748
"""Update the feature flags for this branch.
2750
:param updated_flags: Dictionary mapping feature names to necessities
2751
A necessity can be None to indicate the feature should be removed
2753
self._format._update_feature_flags(updated_flags)
2754
self.control_transport.put_bytes('format', self._format.as_string())
2757
2704
class FullHistoryBzrBranch(BzrBranch):
2758
2705
"""Bzr branch which contains the full revision history."""
2865
2812
class BzrBranch8(BzrBranch):
2866
2813
"""A branch that stores tree-reference locations."""
2868
def _open_hook(self, possible_transports=None):
2815
def _open_hook(self):
2869
2816
if self._ignore_fallbacks:
2871
if possible_transports is None:
2872
possible_transports = [self.bzrdir.root_transport]
2874
2819
url = self.get_stacked_on_url()
2875
2820
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2883
2828
raise AssertionError(
2884
2829
"'transform_fallback_location' hook %s returned "
2885
2830
"None, not a URL." % hook_name)
2886
self._activate_fallback_location(url,
2887
possible_transports=possible_transports)
2831
self._activate_fallback_location(url)
2889
2833
def __init__(self, *args, **kwargs):
2890
2834
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
3008
2952
"""See Branch.set_push_location."""
3009
2953
self._master_branch_cache = None
3011
conf = self.get_config_stack()
2955
config = self.get_config()
3012
2956
if location is None:
3013
if not conf.get('bound'):
2957
if config.get_user_option('bound') != 'True':
3016
conf.set('bound', 'False')
2960
config.set_user_option('bound', 'False', warn_masked=True)
3019
2963
self._set_config_location('bound_location', location,
3021
conf.set('bound', 'True')
2965
config.set_user_option('bound', 'True', warn_masked=True)
3024
2968
def _get_bound_location(self, bound):
3025
2969
"""Return the bound location in the config file.
3027
2971
Return None if the bound parameter does not match"""
3028
conf = self.get_config_stack()
3029
if conf.get('bound') != bound:
2972
config = self.get_config()
2973
config_bound = (config.get_user_option('bound') == 'True')
2974
if config_bound != bound:
3031
return self._get_config_location('bound_location', config=conf)
2976
return self._get_config_location('bound_location', config=config)
3033
2978
def get_bound_location(self):
3034
"""See Branch.get_bound_location."""
2979
"""See Branch.set_push_location."""
3035
2980
return self._get_bound_location(True)
3037
2982
def get_old_bound_location(self):
3044
2989
## self._check_stackable_repo()
3045
2990
# stacked_on_location is only ever defined in branch.conf, so don't
3046
2991
# waste effort reading the whole stack of config files.
3047
conf = _mod_config.BranchOnlyStack(self)
2992
config = self.get_config()._get_branch_data_config()
3048
2993
stacked_url = self._get_config_location('stacked_on_location',
3050
2995
if stacked_url is None:
3051
2996
raise errors.NotStacked(self)
3052
return stacked_url.encode('utf-8')
3054
2999
@needs_read_lock
3055
3000
def get_rev_id(self, revno, history=None):
3251
3196
# Copy source data into target
3252
3197
new_branch._write_last_revision_info(*branch.last_revision_info())
3253
new_branch.lock_write()
3255
new_branch.set_parent(branch.get_parent())
3256
new_branch.set_bound_location(branch.get_bound_location())
3257
new_branch.set_push_location(branch.get_push_location())
3198
new_branch.set_parent(branch.get_parent())
3199
new_branch.set_bound_location(branch.get_bound_location())
3200
new_branch.set_push_location(branch.get_push_location())
3261
3202
# New branch has no tags by default
3262
3203
new_branch.tags._set_tag_dict({})
3264
3205
# Copying done; now update target format
3265
3206
new_branch._transport.put_bytes('format',
3207
format.get_format_string(),
3267
3208
mode=new_branch.bzrdir._get_file_mode())
3269
3210
# Clean up old files
3270
3211
new_branch._transport.delete('revision-history')
3274
branch.set_parent(None)
3275
except errors.NoSuchFile:
3277
branch.set_bound_location(None)
3213
branch.set_parent(None)
3214
except errors.NoSuchFile:
3216
branch.set_bound_location(None)
3282
3219
class Converter6to7(object):
3286
3223
format = BzrBranchFormat7()
3287
3224
branch._set_config_location('stacked_on_location', '')
3288
3225
# update target format
3289
branch._transport.put_bytes('format', format.as_string())
3226
branch._transport.put_bytes('format', format.get_format_string())
3292
3229
class Converter7to8(object):
3296
3233
format = BzrBranchFormat8()
3297
3234
branch._transport.put_bytes('references', '')
3298
3235
# update target format
3299
branch._transport.put_bytes('format', format.as_string())
3236
branch._transport.put_bytes('format', format.get_format_string())
3302
3239
class InterBranch(InterObject):