173
182
For instance, if the branch is at URL/.bzr/branch,
174
183
Branch.open(URL) -> a Branch instance.
176
control = controldir.ControlDir.open(base, _unsupported,
177
possible_transports=possible_transports)
178
return control.open_branch(unsupported=_unsupported)
185
control = controldir.ControlDir.open(base,
186
possible_transports=possible_transports, _unsupported=_unsupported)
187
return control.open_branch(unsupported=_unsupported,
188
possible_transports=possible_transports)
181
def open_from_transport(transport, name=None, _unsupported=False):
191
def open_from_transport(transport, name=None, _unsupported=False,
192
possible_transports=None):
182
193
"""Open the branch rooted at transport"""
183
194
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
184
return control.open_branch(name=name, unsupported=_unsupported)
195
return control.open_branch(name=name, unsupported=_unsupported,
196
possible_transports=possible_transports)
187
199
def open_containing(url, possible_transports=None):
240
253
raise NotImplementedError(self._get_config)
242
def _get_fallback_repository(self, url):
255
def store_uncommitted(self, creator):
256
"""Store uncommitted changes from a ShelfCreator.
258
:param creator: The ShelfCreator containing uncommitted changes, or
259
None to delete any stored changes.
260
:raises: ChangesAlreadyStored if the branch already has changes.
262
raise NotImplementedError(self.store_uncommitted)
264
def get_unshelver(self, tree):
265
"""Return a shelf.Unshelver for this branch and tree.
267
:param tree: The tree to use to construct the Unshelver.
268
:return: an Unshelver or None if no changes are stored.
270
raise NotImplementedError(self.get_unshelver)
272
def _get_fallback_repository(self, url, possible_transports):
243
273
"""Get the repository we fallback to at url."""
244
274
url = urlutils.join(self.base, url)
245
a_branch = Branch.open(url,
246
possible_transports=[self.bzrdir.root_transport])
275
a_branch = Branch.open(url, possible_transports=possible_transports)
247
276
return a_branch.repository
761
784
"""Print `file` to stdout."""
762
785
raise NotImplementedError(self.print_file)
764
@deprecated_method(deprecated_in((2, 4, 0)))
765
def set_revision_history(self, rev_history):
766
"""See Branch.set_revision_history."""
767
self._set_revision_history(rev_history)
770
def _set_revision_history(self, rev_history):
771
if len(rev_history) == 0:
772
revid = _mod_revision.NULL_REVISION
774
revid = rev_history[-1]
775
if rev_history != self._lefthand_history(revid):
776
raise errors.NotLefthandHistory(rev_history)
777
self.set_last_revision_info(len(rev_history), revid)
778
self._cache_revision_history(rev_history)
779
for hook in Branch.hooks['set_rh']:
780
hook(self, rev_history)
782
787
@needs_write_lock
783
788
def set_last_revision_info(self, revno, revision_id):
784
789
"""Set the last revision of this branch.
1056
1052
def _read_last_revision_info(self):
1057
1053
raise NotImplementedError(self._read_last_revision_info)
1059
@deprecated_method(deprecated_in((2, 4, 0)))
1060
def import_last_revision_info(self, source_repo, revno, revid):
1061
"""Set the last revision info, importing from another repo if necessary.
1063
:param source_repo: Source repository to optionally fetch from
1064
:param revno: Revision number of the new tip
1065
:param revid: Revision id of the new tip
1067
if not self.repository.has_same_location(source_repo):
1068
self.repository.fetch(source_repo, revision_id=revid)
1069
self.set_last_revision_info(revno, revid)
1071
1055
def import_last_revision_info_and_tags(self, source, revno, revid,
1073
1057
"""Set the last revision info, importing from another repo if necessary.
1164
1148
def _set_config_location(self, name, url, config=None,
1165
1149
make_relative=False):
1166
1150
if config is None:
1167
config = self.get_config()
1151
config = self.get_config_stack()
1168
1152
if url is None:
1170
1154
elif make_relative:
1171
1155
url = urlutils.relative_url(self.base, url)
1172
config.set_user_option(name, url, warn_masked=True)
1156
config.set(name, url)
1174
1158
def _get_config_location(self, name, config=None):
1175
1159
if config is None:
1176
config = self.get_config()
1177
location = config.get_user_option(name)
1160
config = self.get_config_stack()
1161
location = config.get(name)
1178
1162
if location == '':
1179
1163
location = None
1180
1164
return location
1182
1166
def get_child_submit_format(self):
1183
1167
"""Return the preferred format of submissions to this branch."""
1184
return self.get_config().get_user_option("child_submit_format")
1168
return self.get_config_stack().get('child_submit_format')
1186
1170
def get_submit_branch(self):
1187
1171
"""Return the submit location of the branch.
1453
1435
t = transport.get_transport(to_location)
1454
1436
t.ensure_base()
1455
1437
format = self._get_checkout_format(lightweight=lightweight)
1439
checkout = format.initialize_on_transport(t)
1440
except errors.AlreadyControlDirError:
1441
# It's fine if the control directory already exists,
1442
# as long as there is no existing branch and working tree.
1443
checkout = controldir.ControlDir.open_from_transport(t)
1445
checkout.open_branch()
1446
except errors.NotBranchError:
1449
raise errors.AlreadyControlDirError(t.base)
1450
if checkout.control_transport.base == self.bzrdir.control_transport.base:
1451
# When checking out to the same control directory,
1452
# always create a lightweight checkout
1456
1455
if lightweight:
1457
checkout = format.initialize_on_transport(t)
1458
from_branch = BranchReferenceFormat().initialize(checkout,
1456
from_branch = checkout.set_branch_reference(target_branch=self)
1461
checkout_branch = controldir.ControlDir.create_branch_convenience(
1462
to_location, force_new_tree=False, format=format)
1463
checkout = checkout_branch.bzrdir
1458
policy = checkout.determine_repository_policy()
1459
repo = policy.acquire_repository()[0]
1460
checkout_branch = checkout.create_branch()
1464
1461
checkout_branch.bind(self)
1465
1462
# pull up to the specified revision_id to set the initial
1466
1463
# branch tip correctly, and seed it with history.
1467
1464
checkout_branch.pull(self, stop_revision=revision_id)
1469
1466
tree = checkout.create_workingtree(revision_id,
1470
1467
from_branch=from_branch,
1471
1468
accelerator_tree=accelerator_tree,
1601
1595
def __ne__(self, other):
1602
1596
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
except errors.NoSuchFile:
1610
raise errors.NotBranchError(path=name, bzrdir=controldir)
1612
format_string = transport.get_bytes("format")
1613
return format_registry.get(format_string)
1614
except errors.NoSuchFile:
1615
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1617
raise errors.UnknownFormatError(format=format_string, kind='branch')
1620
@deprecated_method(deprecated_in((2, 4, 0)))
1621
def get_default_format(klass):
1622
"""Return the current default format."""
1623
return format_registry.get_default()
1626
@deprecated_method(deprecated_in((2, 4, 0)))
1627
def get_formats(klass):
1628
"""Get all the known formats.
1630
Warning: This triggers a load of all lazy registered formats: do not
1631
use except when that is desireed.
1633
return format_registry._get_all()
1635
1598
def get_reference(self, controldir, name=None):
1636
1599
"""Get the target reference of the branch in controldir.
2013
1946
self.revision_id)
2016
class BranchFormatMetadir(BranchFormat):
2017
"""Common logic for meta-dir based branch formats."""
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1950
"""Base class for branch formats that live in meta directories.
1954
BranchFormat.__init__(self)
1955
bzrdir.BzrFormat.__init__(self)
1958
def find_format(klass, controldir, name=None):
1959
"""Return the format for the branch object in controldir."""
1961
transport = controldir.get_branch_transport(None, name=name)
1962
except errors.NoSuchFile:
1963
raise errors.NotBranchError(path=name, bzrdir=controldir)
1965
format_string = transport.get_bytes("format")
1966
except errors.NoSuchFile:
1967
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1968
return klass._find_format(format_registry, 'branch', format_string)
2019
1970
def _branch_class(self):
2020
1971
"""What class to instantiate on open calls."""
2099
2048
def supports_leaving_lock(self):
2103
class BzrBranchFormat5(BranchFormatMetadir):
2104
"""Bzr branch format 5.
2107
- a revision-history file.
2109
- a lock dir guarding the branch itself
2110
- all of this stored in a branch/ subdirectory
2111
- works with shared repositories.
2113
This format is new in bzr 0.8.
2116
def _branch_class(self):
2119
def get_format_string(self):
2120
"""See BranchFormat.get_format_string()."""
2121
return "Bazaar-NG branch format 5\n"
2123
def get_format_description(self):
2124
"""See BranchFormat.get_format_description()."""
2125
return "Branch format 5"
2127
def initialize(self, a_bzrdir, name=None, repository=None,
2128
append_revisions_only=None):
2129
"""Create a branch of this format in a_bzrdir."""
2130
if append_revisions_only:
2131
raise errors.UpgradeRequired(a_bzrdir.user_url)
2132
utf8_files = [('revision-history', ''),
2133
('branch-name', ''),
2135
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2137
def supports_tags(self):
2051
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2053
BranchFormat.check_support_status(self,
2054
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2056
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2057
recommend_upgrade=recommend_upgrade, basedir=basedir)
2141
2060
class BzrBranchFormat6(BranchFormatMetadir):
2298
2221
mutter('creating branch reference in %s', a_bzrdir.user_url)
2299
2222
if a_bzrdir._format.fixed_components:
2300
2223
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2225
name = a_bzrdir._get_selected_branch()
2301
2226
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2302
2227
branch_transport.put_bytes('location',
2303
target_branch.bzrdir.user_url)
2304
branch_transport.put_bytes('format', self.get_format_string())
2306
a_bzrdir, name, _found=True,
2228
target_branch.user_url)
2229
branch_transport.put_bytes('format', self.as_string())
2230
branch = self.open(a_bzrdir, name, _found=True,
2307
2231
possible_transports=[target_branch.bzrdir.root_transport])
2308
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2433
2359
def __init__(self, _format=None,
2434
2360
_control_files=None, a_bzrdir=None, name=None,
2435
_repository=None, ignore_fallbacks=False):
2361
_repository=None, ignore_fallbacks=False,
2362
possible_transports=None):
2436
2363
"""Create new branch object at a particular location."""
2437
2364
if a_bzrdir is None:
2438
2365
raise ValueError('a_bzrdir must be supplied')
2440
self.bzrdir = a_bzrdir
2441
self._base = self.bzrdir.transport.clone('..').base
2367
raise ValueError('name must be supplied')
2368
self.bzrdir = a_bzrdir
2369
self._user_transport = self.bzrdir.transport.clone('..')
2371
self._user_transport.set_segment_parameter(
2372
"branch", urlutils.escape(name))
2373
self._base = self._user_transport.base
2442
2374
self.name = name
2443
# XXX: We should be able to just do
2444
# self.base = self.bzrdir.root_transport.base
2445
# but this does not quite work yet -- mbp 20080522
2446
2375
self._format = _format
2447
2376
if _control_files is None:
2448
2377
raise ValueError('BzrBranch _control_files is None')
2449
2378
self.control_files = _control_files
2450
2379
self._transport = _control_files._transport
2451
2380
self.repository = _repository
2452
Branch.__init__(self)
2381
self.conf_store = None
2382
Branch.__init__(self, possible_transports)
2454
2384
def __str__(self):
2455
if self.name is None:
2456
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2458
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2385
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2461
2387
__repr__ = __str__
2467
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
2469
2399
def _get_config(self):
2470
2400
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2402
def _get_config_store(self):
2403
if self.conf_store is None:
2404
self.conf_store = _mod_config.BranchStore(self)
2405
return self.conf_store
2407
def _uncommitted_branch(self):
2408
"""Return the branch that may contain uncommitted changes."""
2409
master = self.get_master_branch()
2410
if master is not None:
2415
def store_uncommitted(self, creator):
2416
"""Store uncommitted changes from a ShelfCreator.
2418
:param creator: The ShelfCreator containing uncommitted changes, or
2419
None to delete any stored changes.
2420
:raises: ChangesAlreadyStored if the branch already has changes.
2422
branch = self._uncommitted_branch()
2424
branch._transport.delete('stored-transform')
2426
if branch._transport.has('stored-transform'):
2427
raise errors.ChangesAlreadyStored
2428
transform = StringIO()
2429
creator.write_shelf(transform)
2431
branch._transport.put_file('stored-transform', transform)
2433
def get_unshelver(self, tree):
2434
"""Return a shelf.Unshelver for this branch and tree.
2436
:param tree: The tree to use to construct the Unshelver.
2437
:return: an Unshelver or None if no changes are stored.
2439
branch = self._uncommitted_branch()
2441
transform = branch._transport.get('stored-transform')
2442
except errors.NoSuchFile:
2444
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2472
2446
def is_locked(self):
2473
2447
return self.control_files.is_locked()
2703
2669
self._transport.put_bytes('last-revision', out_string,
2704
2670
mode=self.bzrdir._get_file_mode())
2707
class FullHistoryBzrBranch(BzrBranch):
2708
"""Bzr branch which contains the full revision history."""
2711
def set_last_revision_info(self, revno, revision_id):
2712
if not revision_id or not isinstance(revision_id, basestring):
2713
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2714
revision_id = _mod_revision.ensure_null(revision_id)
2715
# this old format stores the full history, but this api doesn't
2716
# provide it, so we must generate, and might as well check it's
2718
history = self._lefthand_history(revision_id)
2719
if len(history) != revno:
2720
raise AssertionError('%d != %d' % (len(history), revno))
2721
self._set_revision_history(history)
2723
def _read_last_revision_info(self):
2724
rh = self._revision_history()
2727
return (revno, rh[-1])
2729
return (0, _mod_revision.NULL_REVISION)
2731
@deprecated_method(deprecated_in((2, 4, 0)))
2733
def set_revision_history(self, rev_history):
2734
"""See Branch.set_revision_history."""
2735
self._set_revision_history(rev_history)
2737
def _set_revision_history(self, rev_history):
2738
if 'evil' in debug.debug_flags:
2739
mutter_callsite(3, "set_revision_history scales with history.")
2740
check_not_reserved_id = _mod_revision.check_not_reserved_id
2741
for rev_id in rev_history:
2742
check_not_reserved_id(rev_id)
2743
if Branch.hooks['post_change_branch_tip']:
2744
# Don't calculate the last_revision_info() if there are no hooks
2746
old_revno, old_revid = self.last_revision_info()
2747
if len(rev_history) == 0:
2748
revid = _mod_revision.NULL_REVISION
2750
revid = rev_history[-1]
2751
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2752
self._write_revision_history(rev_history)
2753
self._clear_cached_state()
2754
self._cache_revision_history(rev_history)
2755
for hook in Branch.hooks['set_rh']:
2756
hook(self, rev_history)
2757
if Branch.hooks['post_change_branch_tip']:
2758
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2760
def _write_revision_history(self, history):
2761
"""Factored out of set_revision_history.
2763
This performs the actual writing to disk.
2764
It is intended to be called by set_revision_history."""
2765
self._transport.put_bytes(
2766
'revision-history', '\n'.join(history),
2767
mode=self.bzrdir._get_file_mode())
2769
def _gen_revision_history(self):
2770
history = self._transport.get_bytes('revision-history').split('\n')
2771
if history[-1:] == ['']:
2772
# There shouldn't be a trailing newline, but just in case.
2776
def _synchronize_history(self, destination, revision_id):
2777
if not isinstance(destination, FullHistoryBzrBranch):
2778
super(BzrBranch, self)._synchronize_history(
2779
destination, revision_id)
2781
if revision_id == _mod_revision.NULL_REVISION:
2784
new_history = self._revision_history()
2785
if revision_id is not None and new_history != []:
2787
new_history = new_history[:new_history.index(revision_id) + 1]
2789
rev = self.repository.get_revision(revision_id)
2790
new_history = rev.get_history(self.repository)[1:]
2791
destination._set_revision_history(new_history)
2794
def generate_revision_history(self, revision_id, last_rev=None,
2796
"""Create a new revision history that will finish with revision_id.
2798
:param revision_id: the new tip to use.
2799
:param last_rev: The previous last_revision. If not None, then this
2800
must be a ancestory of revision_id, or DivergedBranches is raised.
2801
:param other_branch: The other branch that DivergedBranches should
2802
raise with respect to.
2673
def update_feature_flags(self, updated_flags):
2674
"""Update the feature flags for this branch.
2676
:param updated_flags: Dictionary mapping feature names to necessities
2677
A necessity can be None to indicate the feature should be removed
2804
self._set_revision_history(self._lefthand_history(revision_id,
2805
last_rev, other_branch))
2808
class BzrBranch5(FullHistoryBzrBranch):
2809
"""A format 5 branch. This supports new features over plain branches.
2811
It has support for a master_branch which is the data for bound branches.
2679
self._format._update_feature_flags(updated_flags)
2680
self.control_transport.put_bytes('format', self._format.as_string())
2815
2683
class BzrBranch8(BzrBranch):
2816
2684
"""A branch that stores tree-reference locations."""
2818
def _open_hook(self):
2686
def _open_hook(self, possible_transports=None):
2819
2687
if self._ignore_fallbacks:
2689
if possible_transports is None:
2690
possible_transports = [self.bzrdir.root_transport]
2822
2692
url = self.get_stacked_on_url()
2823
2693
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2955
2826
"""See Branch.set_push_location."""
2956
2827
self._master_branch_cache = None
2958
config = self.get_config()
2829
conf = self.get_config_stack()
2959
2830
if location is None:
2960
if config.get_user_option('bound') != 'True':
2831
if not conf.get('bound'):
2963
config.set_user_option('bound', 'False', warn_masked=True)
2834
conf.set('bound', 'False')
2966
2837
self._set_config_location('bound_location', location,
2968
config.set_user_option('bound', 'True', warn_masked=True)
2839
conf.set('bound', 'True')
2971
2842
def _get_bound_location(self, bound):
2972
2843
"""Return the bound location in the config file.
2974
2845
Return None if the bound parameter does not match"""
2975
config = self.get_config()
2976
config_bound = (config.get_user_option('bound') == 'True')
2977
if config_bound != bound:
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
2979
return self._get_config_location('bound_location', config=config)
2849
return self._get_config_location('bound_location', config=conf)
2981
2851
def get_bound_location(self):
2982
"""See Branch.set_push_location."""
2852
"""See Branch.get_bound_location."""
2983
2853
return self._get_bound_location(True)
2985
2855
def get_old_bound_location(self):
3199
3053
# Copy source data into target
3200
3054
new_branch._write_last_revision_info(*branch.last_revision_info())
3201
new_branch.set_parent(branch.get_parent())
3202
new_branch.set_bound_location(branch.get_bound_location())
3203
new_branch.set_push_location(branch.get_push_location())
3055
new_branch.lock_write()
3057
new_branch.set_parent(branch.get_parent())
3058
new_branch.set_bound_location(branch.get_bound_location())
3059
new_branch.set_push_location(branch.get_push_location())
3205
3063
# New branch has no tags by default
3206
3064
new_branch.tags._set_tag_dict({})
3208
3066
# Copying done; now update target format
3209
3067
new_branch._transport.put_bytes('format',
3210
format.get_format_string(),
3211
3069
mode=new_branch.bzrdir._get_file_mode())
3213
3071
# Clean up old files
3214
3072
new_branch._transport.delete('revision-history')
3216
branch.set_parent(None)
3217
except errors.NoSuchFile:
3219
branch.set_bound_location(None)
3076
branch.set_parent(None)
3077
except errors.NoSuchFile:
3079
branch.set_bound_location(None)
3222
3084
class Converter6to7(object):
3469
3340
result.target_branch = self.target
3470
3341
result.old_revno, result.old_revid = self.target.last_revision_info()
3471
3342
self.source.update_references(self.target)
3343
overwrite = _fix_overwrite_type(overwrite)
3472
3344
if result.old_revid != stop_revision:
3473
3345
# We assume that during 'push' this repository is closer than
3475
3347
graph = self.source.repository.get_graph(self.target.repository)
3476
self._update_revisions(stop_revision, overwrite=overwrite,
3348
self._update_revisions(stop_revision,
3349
overwrite=("history" in overwrite),
3478
3351
if self.source._push_should_merge_tags():
3479
3352
result.tag_updates, result.tag_conflicts = (
3480
self.source.tags.merge_to(self.target.tags, overwrite))
3353
self.source.tags.merge_to(
3354
self.target.tags, "tags" in overwrite))
3481
3355
result.new_revno, result.new_revid = self.target.last_revision_info()
3561
3435
# -- JRV20090506
3562
3436
result.old_revno, result.old_revid = \
3563
3437
self.target.last_revision_info()
3564
self._update_revisions(stop_revision, overwrite=overwrite,
3438
overwrite = _fix_overwrite_type(overwrite)
3439
self._update_revisions(stop_revision,
3440
overwrite=("history" in overwrite),
3566
3442
# TODO: The old revid should be specified when merging tags,
3567
3443
# so a tags implementation that versions tags can only
3568
3444
# pull in the most recent changes. -- JRV20090506
3569
3445
result.tag_updates, result.tag_conflicts = (
3570
self.source.tags.merge_to(self.target.tags, overwrite,
3446
self.source.tags.merge_to(self.target.tags,
3447
"tags" in overwrite,
3571
3448
ignore_master=not merge_tags_to_master))
3572
3449
result.new_revno, result.new_revid = self.target.last_revision_info()
3573
3450
if _hook_master: