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
21
18
from cStringIO import StringIO
23
21
from bzrlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
23
from itertools import chain
26
24
from bzrlib import (
31
config as _mod_config,
40
revision as _mod_revision,
27
config as _mod_config,
36
revision as _mod_revision,
44
from bzrlib.config import BranchConfig, TransportConfig
45
from bzrlib.tag import (
49
from bzrlib.i18n import gettext, ngettext
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
53
# is guaranteed to be registered.
56
51
from bzrlib import (
60
54
from bzrlib.decorators import (
105
104
self._last_revision_info_cache = None
106
105
self._master_branch_cache = None
107
106
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
109
108
hooks = Branch.hooks['open']
110
109
for hook in hooks:
113
def _open_hook(self, possible_transports):
112
def _open_hook(self):
114
113
"""Called by init to allow simpler extension of the base class."""
116
def _activate_fallback_location(self, url, possible_transports):
115
def _activate_fallback_location(self, url):
117
116
"""Activate the branch/repository from url as a fallback repository."""
118
117
for existing_fallback_repo in self.repository._fallback_repositories:
119
118
if existing_fallback_repo.user_url == url:
120
119
# This fallback is already configured. This probably only
121
# happens because ControlDir.sprout is a horrible mess. To avoid
120
# happens because BzrDir.sprout is a horrible mess. To avoid
122
121
# confusing _unstack we don't add this a second time.
123
122
mutter('duplicate activation of fallback %r on %r', url, self)
125
repo = self._get_fallback_repository(url, possible_transports)
124
repo = self._get_fallback_repository(url)
126
125
if repo.has_same_location(self.repository):
127
126
raise errors.UnstackableLocationError(self.user_url, url)
128
127
self.repository.add_fallback_repository(repo)
182
181
For instance, if the branch is at URL/.bzr/branch,
183
182
Branch.open(URL) -> a Branch instance.
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)
184
control = bzrdir.BzrDir.open(base, _unsupported,
185
possible_transports=possible_transports)
186
return control.open_branch(unsupported=_unsupported)
191
def open_from_transport(transport, name=None, _unsupported=False,
192
possible_transports=None):
189
def open_from_transport(transport, name=None, _unsupported=False):
193
190
"""Open the branch rooted at transport"""
194
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
195
return control.open_branch(name=name, unsupported=_unsupported,
196
possible_transports=possible_transports)
191
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
192
return control.open_branch(name=name, unsupported=_unsupported)
199
195
def open_containing(url, possible_transports=None):
253
238
raise NotImplementedError(self._get_config)
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):
240
def _get_fallback_repository(self, url):
273
241
"""Get the repository we fallback to at url."""
274
242
url = urlutils.join(self.base, url)
275
a_branch = Branch.open(url, possible_transports=possible_transports)
243
a_branch = Branch.open(url,
244
possible_transports=[self.bzrdir.root_transport])
276
245
return a_branch.repository
545
512
rev_iter = iter(merge_sorted_revisions)
546
513
if start_revision_id is not None:
547
514
for node in rev_iter:
515
rev_id = node.key[-1]
549
516
if rev_id != start_revision_id:
552
519
# The decision to include the start or not
553
520
# depends on the stop_rule if a stop is provided
554
521
# so pop this node back into the iterator
555
rev_iter = itertools.chain(iter([node]), rev_iter)
522
rev_iter = chain(iter([node]), rev_iter)
557
524
if stop_revision_id is None:
558
525
# Yield everything
559
526
for node in rev_iter:
527
rev_id = node.key[-1]
561
528
yield (rev_id, node.merge_depth, node.revno,
562
529
node.end_of_merge)
563
530
elif stop_rule == 'exclude':
564
531
for node in rev_iter:
532
rev_id = node.key[-1]
566
533
if rev_id == stop_revision_id:
568
535
yield (rev_id, node.merge_depth, node.revno,
569
536
node.end_of_merge)
570
537
elif stop_rule == 'include':
571
538
for node in rev_iter:
539
rev_id = node.key[-1]
573
540
yield (rev_id, node.merge_depth, node.revno,
574
541
node.end_of_merge)
575
542
if rev_id == stop_revision_id:
784
746
"""Print `file` to stdout."""
785
747
raise NotImplementedError(self.print_file)
749
@deprecated_method(deprecated_in((2, 4, 0)))
750
def set_revision_history(self, rev_history):
751
"""See Branch.set_revision_history."""
752
self._set_revision_history(rev_history)
755
def _set_revision_history(self, rev_history):
756
if len(rev_history) == 0:
757
revid = _mod_revision.NULL_REVISION
759
revid = rev_history[-1]
760
if rev_history != self._lefthand_history(revid):
761
raise errors.NotLefthandHistory(rev_history)
762
self.set_last_revision_info(len(rev_history), revid)
763
self._cache_revision_history(rev_history)
764
for hook in Branch.hooks['set_rh']:
765
hook(self, rev_history)
787
767
@needs_write_lock
788
768
def set_last_revision_info(self, revno, revision_id):
789
769
"""Set the last revision of this branch.
796
776
configured to check constraints on history, in which case this may not
799
raise NotImplementedError(self.set_last_revision_info)
779
raise NotImplementedError(self.last_revision_info)
801
781
@needs_write_lock
802
782
def generate_revision_history(self, revision_id, last_rev=None,
803
783
other_branch=None):
804
784
"""See Branch.generate_revision_history"""
805
graph = self.repository.get_graph()
806
(last_revno, last_revid) = self.last_revision_info()
807
known_revision_ids = [
808
(last_revid, last_revno),
809
(_mod_revision.NULL_REVISION, 0),
811
if last_rev is not None:
812
if not graph.is_ancestor(last_rev, revision_id):
813
# our previous tip is not merged into stop_revision
814
raise errors.DivergedBranches(self, other_branch)
815
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
785
# FIXME: This shouldn't have to fetch the entire history
786
history = self._lefthand_history(revision_id, last_rev, other_branch)
816
788
self.set_last_revision_info(revno, revision_id)
789
self._cache_revision_history(history)
818
791
@needs_write_lock
819
792
def set_parent(self, url):
1052
1028
def _read_last_revision_info(self):
1053
1029
raise NotImplementedError(self._read_last_revision_info)
1031
@deprecated_method(deprecated_in((2, 4, 0)))
1032
def import_last_revision_info(self, source_repo, revno, revid):
1033
"""Set the last revision info, importing from another repo if necessary.
1035
:param source_repo: Source repository to optionally fetch from
1036
:param revno: Revision number of the new tip
1037
:param revid: Revision id of the new tip
1039
if not self.repository.has_same_location(source_repo):
1040
self.repository.fetch(source_repo, revision_id=revid)
1041
self.set_last_revision_info(revno, revid)
1055
1043
def import_last_revision_info_and_tags(self, source, revno, revid,
1057
1045
"""Set the last revision info, importing from another repo if necessary.
1110
1098
stop_revision=stop_revision,
1111
1099
possible_transports=possible_transports, *args, **kwargs)
1113
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1101
def push(self, target, overwrite=False, stop_revision=None, *args,
1115
1103
"""Mirror this branch into target.
1117
1105
This branch is considered to be 'local', having low latency.
1119
1107
return InterBranch.get(self, target).push(overwrite, stop_revision,
1120
lossy, *args, **kwargs)
1110
def lossy_push(self, target, stop_revision=None):
1111
"""Push deltas into another branch.
1113
:note: This does not, like push, retain the revision ids from
1114
the source branch and will, rather than adding bzr-specific
1115
metadata, push only those semantics of the revision that can be
1116
natively represented by this branch' VCS.
1118
:param target: Target branch
1119
:param stop_revision: Revision to push, defaults to last revision.
1120
:return: BranchPushResult with an extra member revidmap:
1121
A dictionary mapping revision ids from the target branch
1122
to new revision ids in the target branch, for each
1123
revision that was pushed.
1125
inter = InterBranch.get(self, target)
1126
lossy_push = getattr(inter, "lossy_push", None)
1127
if lossy_push is None:
1128
raise errors.LossyPushToSameVCS(self, target)
1129
return lossy_push(stop_revision)
1122
1131
def basis_tree(self):
1123
1132
"""Return `Tree` object for last revision."""
1148
1157
def _set_config_location(self, name, url, config=None,
1149
1158
make_relative=False):
1150
1159
if config is None:
1151
config = self.get_config_stack()
1160
config = self.get_config()
1152
1161
if url is None:
1154
1163
elif make_relative:
1155
1164
url = urlutils.relative_url(self.base, url)
1156
config.set(name, url)
1165
config.set_user_option(name, url, warn_masked=True)
1158
1167
def _get_config_location(self, name, config=None):
1159
1168
if config is None:
1160
config = self.get_config_stack()
1161
location = config.get(name)
1169
config = self.get_config()
1170
location = config.get_user_option(name)
1162
1171
if location == '':
1163
1172
location = None
1164
1173
return location
1166
1175
def get_child_submit_format(self):
1167
1176
"""Return the preferred format of submissions to this branch."""
1168
return self.get_config_stack().get('child_submit_format')
1177
return self.get_config().get_user_option("child_submit_format")
1170
1179
def get_submit_branch(self):
1171
1180
"""Return the submit location of the branch.
1435
1446
t = transport.get_transport(to_location)
1436
1447
t.ensure_base()
1437
format = self._get_checkout_format(lightweight=lightweight)
1449
format = self._get_checkout_format()
1439
1450
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
from_branch = checkout.set_branch_reference(target_branch=self)
1451
from_branch = BranchReferenceFormat().initialize(checkout,
1458
policy = checkout.determine_repository_policy()
1459
repo = policy.acquire_repository()[0]
1460
checkout_branch = checkout.create_branch()
1454
format = self._get_checkout_format()
1455
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1456
to_location, force_new_tree=False, format=format)
1457
checkout = checkout_branch.bzrdir
1461
1458
checkout_branch.bind(self)
1462
1459
# pull up to the specified revision_id to set the initial
1463
1460
# branch tip correctly, and seed it with history.
1464
1461
checkout_branch.pull(self, stop_revision=revision_id)
1466
1463
tree = checkout.create_workingtree(revision_id,
1467
1464
from_branch=from_branch,
1468
1465
accelerator_tree=accelerator_tree,
1589
1583
object will be created every time regardless.
1586
can_set_append_revisions_only = True
1592
1588
def __eq__(self, other):
1593
1589
return self.__class__ is other.__class__
1595
1591
def __ne__(self, other):
1596
1592
return not (self == other)
1598
def get_reference(self, controldir, name=None):
1599
"""Get the target reference of the branch in controldir.
1595
def find_format(klass, a_bzrdir, name=None):
1596
"""Return the format for the branch object in a_bzrdir."""
1598
transport = a_bzrdir.get_branch_transport(None, name=name)
1599
format_string = transport.get_bytes("format")
1600
return format_registry.get(format_string)
1601
except errors.NoSuchFile:
1602
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1604
raise errors.UnknownFormatError(format=format_string, kind='branch')
1607
@deprecated_method(deprecated_in((2, 4, 0)))
1608
def get_default_format(klass):
1609
"""Return the current default format."""
1610
return format_registry.get_default()
1613
@deprecated_method(deprecated_in((2, 4, 0)))
1614
def get_formats(klass):
1615
"""Get all the known formats.
1617
Warning: This triggers a load of all lazy registered formats: do not
1618
use except when that is desireed.
1620
return format_registry._get_all()
1622
def get_reference(self, a_bzrdir, name=None):
1623
"""Get the target reference of the branch in a_bzrdir.
1601
1625
format probing must have been completed before calling
1602
1626
this method - it is assumed that the format of the branch
1603
in controldir is correct.
1627
in a_bzrdir is correct.
1605
:param controldir: The controldir to get the branch data from.
1629
:param a_bzrdir: The bzrdir to get the branch data from.
1606
1630
:param name: Name of the colocated branch to fetch
1607
1631
:return: None if the branch is not a reference branch.
1612
def set_reference(self, controldir, name, to_branch):
1613
"""Set the target reference of the branch in controldir.
1636
def set_reference(self, a_bzrdir, name, to_branch):
1637
"""Set the target reference of the branch in a_bzrdir.
1615
1639
format probing must have been completed before calling
1616
1640
this method - it is assumed that the format of the branch
1617
in controldir is correct.
1641
in a_bzrdir is correct.
1619
:param controldir: The controldir to set the branch reference for.
1643
:param a_bzrdir: The bzrdir to set the branch reference for.
1620
1644
:param name: Name of colocated branch to set, None for default
1621
1645
:param to_branch: branch that the checkout is to reference
1623
1647
raise NotImplementedError(self.set_reference)
1649
def get_format_string(self):
1650
"""Return the ASCII format string that identifies this format."""
1651
raise NotImplementedError(self.get_format_string)
1625
1653
def get_format_description(self):
1626
1654
"""Return the short format description for this format."""
1627
1655
raise NotImplementedError(self.get_format_description)
1629
def _run_post_branch_init_hooks(self, controldir, name, branch):
1657
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1630
1658
hooks = Branch.hooks['post_branch_init']
1633
params = BranchInitHookParams(self, controldir, name, branch)
1661
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1634
1662
for hook in hooks:
1637
def initialize(self, controldir, name=None, repository=None,
1638
append_revisions_only=None):
1639
"""Create a branch of this format in controldir.
1665
def initialize(self, a_bzrdir, name=None, repository=None):
1666
"""Create a branch of this format in a_bzrdir.
1641
1668
:param name: Name of the colocated branch to create.
1643
1670
raise NotImplementedError(self.initialize)
1946
1991
self.revision_id)
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)
1994
class BranchFormatMetadir(BranchFormat):
1995
"""Common logic for meta-dir based branch formats."""
1970
1997
def _branch_class(self):
1971
1998
"""What class to instantiate on open calls."""
1972
1999
raise NotImplementedError(self._branch_class)
1974
def _get_initial_config(self, append_revisions_only=None):
1975
if append_revisions_only:
1976
return "append_revisions_only = True\n"
1978
# Avoid writing anything if append_revisions_only is disabled,
1979
# as that is the default.
1982
2001
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1983
2002
repository=None):
1984
"""Initialize a branch in a control dir, with specified files
2003
"""Initialize a branch in a bzrdir, with specified files
1986
2005
:param a_bzrdir: The bzrdir to initialize the branch in
1987
2006
:param utf8_files: The files to create as a list of
2048
2068
def supports_leaving_lock(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)
2072
class BzrBranchFormat5(BranchFormatMetadir):
2073
"""Bzr branch format 5.
2076
- a revision-history file.
2078
- a lock dir guarding the branch itself
2079
- all of this stored in a branch/ subdirectory
2080
- works with shared repositories.
2082
This format is new in bzr 0.8.
2085
def _branch_class(self):
2088
def get_format_string(self):
2089
"""See BranchFormat.get_format_string()."""
2090
return "Bazaar-NG branch format 5\n"
2092
def get_format_description(self):
2093
"""See BranchFormat.get_format_description()."""
2094
return "Branch format 5"
2096
def initialize(self, a_bzrdir, name=None, repository=None):
2097
"""Create a branch of this format in a_bzrdir."""
2098
utf8_files = [('revision-history', ''),
2099
('branch-name', ''),
2101
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2103
def supports_tags(self):
2060
2107
class BzrBranchFormat6(BranchFormatMetadir):
2080
2126
"""See BranchFormat.get_format_description()."""
2081
2127
return "Branch format 6"
2083
def initialize(self, a_bzrdir, name=None, repository=None,
2084
append_revisions_only=None):
2129
def initialize(self, a_bzrdir, name=None, repository=None):
2085
2130
"""Create a branch of this format in a_bzrdir."""
2086
2131
utf8_files = [('last-revision', '0 null:\n'),
2088
self._get_initial_config(append_revisions_only)),
2132
('branch.conf', ''),
2091
2135
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2093
2137
def make_tags(self, branch):
2094
2138
"""See bzrlib.branch.BranchFormat.make_tags()."""
2095
return _mod_tag.BasicTags(branch)
2139
return BasicTags(branch)
2097
2141
def supports_set_append_revisions_only(self):
2212
2249
location = transport.put_bytes('location', to_branch.base)
2214
2251
def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
repository=None, append_revisions_only=None):
2216
2253
"""Create a branch of this format in a_bzrdir."""
2217
2254
if target_branch is None:
2218
2255
# this format does not implement branch itself, thus the implicit
2219
2256
# creation contract must see it as uninitializable
2220
2257
raise errors.UninitializableFormat(self)
2221
2258
mutter('creating branch reference in %s', a_bzrdir.user_url)
2222
if a_bzrdir._format.fixed_components:
2223
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2225
name = a_bzrdir._get_selected_branch()
2226
2259
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2227
2260
branch_transport.put_bytes('location',
2228
target_branch.user_url)
2229
branch_transport.put_bytes('format', self.as_string())
2230
branch = self.open(a_bzrdir, name, _found=True,
2261
target_branch.bzrdir.user_url)
2262
branch_transport.put_bytes('format', self.get_format_string())
2264
a_bzrdir, name, _found=True,
2231
2265
possible_transports=[target_branch.bzrdir.root_transport])
2232
2266
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2270
super(BranchReferenceFormat, self).__init__()
2271
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2272
self._matchingbzrdir.set_branch_format(self)
2235
2274
def _make_reference_clone_function(format, a_branch):
2236
2275
"""Create a clone() routine for a branch dynamically."""
2237
2276
def clone(to_bzrdir, revision_id=None,
2260
2299
:param possible_transports: An optional reusable transports list.
2263
name = a_bzrdir._get_selected_branch()
2265
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2302
format = BranchFormat.find_format(a_bzrdir, name=name)
2266
2303
if format.__class__ != self.__class__:
2267
2304
raise AssertionError("wrong format %r found for %r" %
2268
2305
(format, self))
2269
2306
if location is None:
2270
2307
location = self.get_reference(a_bzrdir, name)
2271
real_bzrdir = controldir.ControlDir.open(
2308
real_bzrdir = bzrdir.BzrDir.open(
2272
2309
location, possible_transports=possible_transports)
2273
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2274
possible_transports=possible_transports)
2310
result = real_bzrdir.open_branch(name=name,
2311
ignore_fallbacks=ignore_fallbacks)
2275
2312
# this changes the behaviour of result.clone to create a new reference
2276
2313
# rather than a copy of the content of the branch.
2277
2314
# I did not use a proxy object because that needs much more extensive
2359
2396
def __init__(self, _format=None,
2360
2397
_control_files=None, a_bzrdir=None, name=None,
2361
_repository=None, ignore_fallbacks=False,
2362
possible_transports=None):
2398
_repository=None, ignore_fallbacks=False):
2363
2399
"""Create new branch object at a particular location."""
2364
2400
if a_bzrdir is None:
2365
2401
raise ValueError('a_bzrdir must be supplied')
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
2403
self.bzrdir = a_bzrdir
2404
self._base = self.bzrdir.transport.clone('..').base
2374
2405
self.name = name
2406
# XXX: We should be able to just do
2407
# self.base = self.bzrdir.root_transport.base
2408
# but this does not quite work yet -- mbp 20080522
2375
2409
self._format = _format
2376
2410
if _control_files is None:
2377
2411
raise ValueError('BzrBranch _control_files is None')
2378
2412
self.control_files = _control_files
2379
2413
self._transport = _control_files._transport
2380
2414
self.repository = _repository
2381
self.conf_store = None
2382
Branch.__init__(self, possible_transports)
2415
Branch.__init__(self)
2384
2417
def __str__(self):
2385
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2418
if self.name is None:
2419
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2421
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2387
2424
__repr__ = __str__
2393
2430
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
2399
2432
def _get_config(self):
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)
2433
return TransportConfig(self._transport, 'branch.conf')
2446
2435
def is_locked(self):
2447
2436
return self.control_files.is_locked()
2669
2666
self._transport.put_bytes('last-revision', out_string,
2670
2667
mode=self.bzrdir._get_file_mode())
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
2670
class FullHistoryBzrBranch(BzrBranch):
2671
"""Bzr branch which contains the full revision history."""
2674
def set_last_revision_info(self, revno, revision_id):
2675
if not revision_id or not isinstance(revision_id, basestring):
2676
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2677
revision_id = _mod_revision.ensure_null(revision_id)
2678
# this old format stores the full history, but this api doesn't
2679
# provide it, so we must generate, and might as well check it's
2681
history = self._lefthand_history(revision_id)
2682
if len(history) != revno:
2683
raise AssertionError('%d != %d' % (len(history), revno))
2684
self._set_revision_history(history)
2686
def _read_last_revision_info(self):
2687
rh = self.revision_history()
2690
return (revno, rh[-1])
2692
return (0, _mod_revision.NULL_REVISION)
2694
@deprecated_method(deprecated_in((2, 4, 0)))
2696
def set_revision_history(self, rev_history):
2697
"""See Branch.set_revision_history."""
2698
self._set_revision_history(rev_history)
2700
def _set_revision_history(self, rev_history):
2701
if 'evil' in debug.debug_flags:
2702
mutter_callsite(3, "set_revision_history scales with history.")
2703
check_not_reserved_id = _mod_revision.check_not_reserved_id
2704
for rev_id in rev_history:
2705
check_not_reserved_id(rev_id)
2706
if Branch.hooks['post_change_branch_tip']:
2707
# Don't calculate the last_revision_info() if there are no hooks
2709
old_revno, old_revid = self.last_revision_info()
2710
if len(rev_history) == 0:
2711
revid = _mod_revision.NULL_REVISION
2713
revid = rev_history[-1]
2714
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2715
self._write_revision_history(rev_history)
2716
self._clear_cached_state()
2717
self._cache_revision_history(rev_history)
2718
for hook in Branch.hooks['set_rh']:
2719
hook(self, rev_history)
2720
if Branch.hooks['post_change_branch_tip']:
2721
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2723
def _write_revision_history(self, history):
2724
"""Factored out of set_revision_history.
2726
This performs the actual writing to disk.
2727
It is intended to be called by set_revision_history."""
2728
self._transport.put_bytes(
2729
'revision-history', '\n'.join(history),
2730
mode=self.bzrdir._get_file_mode())
2732
def _gen_revision_history(self):
2733
history = self._transport.get_bytes('revision-history').split('\n')
2734
if history[-1:] == ['']:
2735
# There shouldn't be a trailing newline, but just in case.
2739
def _synchronize_history(self, destination, revision_id):
2740
if not isinstance(destination, FullHistoryBzrBranch):
2741
super(BzrBranch, self)._synchronize_history(
2742
destination, revision_id)
2744
if revision_id == _mod_revision.NULL_REVISION:
2747
new_history = self.revision_history()
2748
if revision_id is not None and new_history != []:
2750
new_history = new_history[:new_history.index(revision_id) + 1]
2752
rev = self.repository.get_revision(revision_id)
2753
new_history = rev.get_history(self.repository)[1:]
2754
destination._set_revision_history(new_history)
2757
def generate_revision_history(self, revision_id, last_rev=None,
2759
"""Create a new revision history that will finish with revision_id.
2761
:param revision_id: the new tip to use.
2762
:param last_rev: The previous last_revision. If not None, then this
2763
must be a ancestory of revision_id, or DivergedBranches is raised.
2764
:param other_branch: The other branch that DivergedBranches should
2765
raise with respect to.
2679
self._format._update_feature_flags(updated_flags)
2680
self.control_transport.put_bytes('format', self._format.as_string())
2767
self._set_revision_history(self._lefthand_history(revision_id,
2768
last_rev, other_branch))
2771
class BzrBranch5(FullHistoryBzrBranch):
2772
"""A format 5 branch. This supports new features over plain branches.
2774
It has support for a master_branch which is the data for bound branches.
2683
2778
class BzrBranch8(BzrBranch):
2684
2779
"""A branch that stores tree-reference locations."""
2686
def _open_hook(self, possible_transports=None):
2781
def _open_hook(self):
2687
2782
if self._ignore_fallbacks:
2689
if possible_transports is None:
2690
possible_transports = [self.bzrdir.root_transport]
2692
2785
url = self.get_stacked_on_url()
2693
2786
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2826
2914
"""See Branch.set_push_location."""
2827
2915
self._master_branch_cache = None
2829
conf = self.get_config_stack()
2917
config = self.get_config()
2830
2918
if location is None:
2831
if not conf.get('bound'):
2919
if config.get_user_option('bound') != 'True':
2834
conf.set('bound', 'False')
2922
config.set_user_option('bound', 'False', warn_masked=True)
2837
2925
self._set_config_location('bound_location', location,
2839
conf.set('bound', 'True')
2927
config.set_user_option('bound', 'True', warn_masked=True)
2842
2930
def _get_bound_location(self, bound):
2843
2931
"""Return the bound location in the config file.
2845
2933
Return None if the bound parameter does not match"""
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
2934
config = self.get_config()
2935
config_bound = (config.get_user_option('bound') == 'True')
2936
if config_bound != bound:
2849
return self._get_config_location('bound_location', config=conf)
2938
return self._get_config_location('bound_location', config=config)
2851
2940
def get_bound_location(self):
2852
"""See Branch.get_bound_location."""
2941
"""See Branch.set_push_location."""
2853
2942
return self._get_bound_location(True)
2855
2944
def get_old_bound_location(self):
2962
3049
:ivar local_branch: target branch if there is a Master, else None
2963
3050
:ivar target_branch: Target/destination branch object. (write locked)
2964
3051
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2965
:ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3054
@deprecated_method(deprecated_in((2, 3, 0)))
3056
"""Return the relative change in revno.
3058
:deprecated: Use `new_revno` and `old_revno` instead.
3060
return self.new_revno - self.old_revno
2968
3062
def report(self, to_file):
2969
tag_conflicts = getattr(self, "tag_conflicts", None)
2970
tag_updates = getattr(self, "tag_updates", None)
2971
3063
if not is_quiet():
2972
if self.old_revid != self.new_revid:
3064
if self.old_revid == self.new_revid:
3065
to_file.write('No revisions to pull.\n')
2973
3067
to_file.write('Now on revision %d.\n' % self.new_revno)
2975
to_file.write('%d tag(s) updated.\n' % len(tag_updates))
2976
if self.old_revid == self.new_revid and not tag_updates:
2977
if not tag_conflicts:
2978
to_file.write('No revisions or tags to pull.\n')
2980
to_file.write('No revisions to pull.\n')
2981
3068
self._show_tag_conficts(to_file)
3000
3087
target, otherwise it will be None.
3090
@deprecated_method(deprecated_in((2, 3, 0)))
3092
"""Return the relative change in revno.
3094
:deprecated: Use `new_revno` and `old_revno` instead.
3096
return self.new_revno - self.old_revno
3003
3098
def report(self, to_file):
3004
# TODO: This function gets passed a to_file, but then
3005
# ignores it and calls note() instead. This is also
3006
# inconsistent with PullResult(), which writes to stdout.
3007
# -- JRV20110901, bug #838853
3008
tag_conflicts = getattr(self, "tag_conflicts", None)
3009
tag_updates = getattr(self, "tag_updates", None)
3011
if self.old_revid != self.new_revid:
3012
note(gettext('Pushed up to revision %d.') % self.new_revno)
3014
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3015
if self.old_revid == self.new_revid and not tag_updates:
3016
if not tag_conflicts:
3017
note(gettext('No new revisions or tags to push.'))
3019
note(gettext('No new revisions to push.'))
3099
"""Write a human-readable description of the result."""
3100
if self.old_revid == self.new_revid:
3101
note('No new revisions to push.')
3103
note('Pushed up to revision %d.' % self.new_revno)
3020
3104
self._show_tag_conficts(to_file)
3053
3137
# Copy source data into target
3054
3138
new_branch._write_last_revision_info(*branch.last_revision_info())
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())
3139
new_branch.set_parent(branch.get_parent())
3140
new_branch.set_bound_location(branch.get_bound_location())
3141
new_branch.set_push_location(branch.get_push_location())
3063
3143
# New branch has no tags by default
3064
3144
new_branch.tags._set_tag_dict({})
3066
3146
# Copying done; now update target format
3067
3147
new_branch._transport.put_bytes('format',
3148
format.get_format_string(),
3069
3149
mode=new_branch.bzrdir._get_file_mode())
3071
3151
# Clean up old files
3072
3152
new_branch._transport.delete('revision-history')
3076
branch.set_parent(None)
3077
except errors.NoSuchFile:
3079
branch.set_bound_location(None)
3154
branch.set_parent(None)
3155
except errors.NoSuchFile:
3157
branch.set_bound_location(None)
3084
3160
class Converter6to7(object):
3088
3164
format = BzrBranchFormat7()
3089
3165
branch._set_config_location('stacked_on_location', '')
3090
3166
# update target format
3091
branch._transport.put_bytes('format', format.as_string())
3167
branch._transport.put_bytes('format', format.get_format_string())
3094
3170
class Converter7to8(object):
3095
"""Perform an in-place upgrade of format 7 to format 8"""
3171
"""Perform an in-place upgrade of format 6 to format 7"""
3097
3173
def convert(self, branch):
3098
3174
format = BzrBranchFormat8()
3099
3175
branch._transport.put_bytes('references', '')
3100
3176
# update target format
3101
branch._transport.put_bytes('format', format.as_string())
3177
branch._transport.put_bytes('format', format.get_format_string())
3180
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3181
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3184
_run_with_write_locked_target will attempt to release the lock it acquires.
3186
If an exception is raised by callable, then that exception *will* be
3187
propagated, even if the unlock attempt raises its own error. Thus
3188
_run_with_write_locked_target should be preferred to simply doing::
3192
return callable(*args, **kwargs)
3197
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3198
# should share code?
3201
result = callable(*args, **kwargs)
3203
exc_info = sys.exc_info()
3207
raise exc_info[0], exc_info[1], exc_info[2]
3104
3213
class InterBranch(InterObject):
3308
3397
if master_branch:
3309
3398
master_branch.unlock()
3311
def push(self, overwrite=False, stop_revision=None, lossy=False,
3400
def push(self, overwrite=False, stop_revision=None,
3312
3401
_override_hook_source_branch=None):
3313
3402
"""See InterBranch.push.
3315
3404
This is the basic concrete implementation of push()
3317
:param _override_hook_source_branch: If specified, run the hooks
3318
passing this Branch as the source, rather than self. This is for
3319
use of RemoteBranch, where push is delegated to the underlying
3406
:param _override_hook_source_branch: If specified, run
3407
the hooks passing this Branch as the source, rather than self.
3408
This is for use of RemoteBranch, where push is delegated to the
3409
underlying vfs-based Branch.
3323
raise errors.LossyPushToSameVCS(self.source, self.target)
3324
3411
# TODO: Public option to disable running hooks - should be trivial but
3327
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3328
op.add_cleanup(self.source.lock_read().unlock)
3329
op.add_cleanup(self.target.lock_write().unlock)
3330
return op.run(overwrite, stop_revision,
3331
_override_hook_source_branch=_override_hook_source_branch)
3413
self.source.lock_read()
3415
return _run_with_write_locked_target(
3416
self.target, self._push_with_bound_branches, overwrite,
3418
_override_hook_source_branch=_override_hook_source_branch)
3420
self.source.unlock()
3333
3422
def _basic_push(self, overwrite, stop_revision):
3334
3423
"""Basic implementation of push without bound branches or hooks.
3340
3429
result.target_branch = self.target
3341
3430
result.old_revno, result.old_revid = self.target.last_revision_info()
3342
3431
self.source.update_references(self.target)
3343
overwrite = _fix_overwrite_type(overwrite)
3344
3432
if result.old_revid != stop_revision:
3345
3433
# We assume that during 'push' this repository is closer than
3347
3435
graph = self.source.repository.get_graph(self.target.repository)
3348
self._update_revisions(stop_revision,
3349
overwrite=("history" in overwrite),
3436
self._update_revisions(stop_revision, overwrite=overwrite,
3351
3438
if self.source._push_should_merge_tags():
3352
result.tag_updates, result.tag_conflicts = (
3353
self.source.tags.merge_to(
3354
self.target.tags, "tags" in overwrite))
3439
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3355
3441
result.new_revno, result.new_revid = self.target.last_revision_info()
3358
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3444
def _push_with_bound_branches(self, overwrite, stop_revision,
3359
3445
_override_hook_source_branch=None):
3360
3446
"""Push from source into target, and into target's master if any.
3373
3459
# be bound to itself? -- mbp 20070507
3374
3460
master_branch = self.target.get_master_branch()
3375
3461
master_branch.lock_write()
3376
operation.add_cleanup(master_branch.unlock)
3377
# push into the master from the source branch.
3378
master_inter = InterBranch.get(self.source, master_branch)
3379
master_inter._basic_push(overwrite, stop_revision)
3380
# and push into the target branch from the source. Note that
3381
# we push from the source branch again, because it's considered
3382
# the highest bandwidth repository.
3383
result = self._basic_push(overwrite, stop_revision)
3384
result.master_branch = master_branch
3385
result.local_branch = self.target
3463
# push into the master from the source branch.
3464
master_inter = InterBranch.get(self.source, master_branch)
3465
master_inter._basic_push(overwrite, stop_revision)
3466
# and push into the target branch from the source. Note that
3467
# we push from the source branch again, because it's considered
3468
# the highest bandwidth repository.
3469
result = self._basic_push(overwrite, stop_revision)
3470
result.master_branch = master_branch
3471
result.local_branch = self.target
3475
master_branch.unlock()
3387
master_branch = None
3388
3477
# no master branch
3389
3478
result = self._basic_push(overwrite, stop_revision)
3390
3479
# TODO: Why set master_branch and local_branch if there's no
3435
3524
# -- JRV20090506
3436
3525
result.old_revno, result.old_revid = \
3437
3526
self.target.last_revision_info()
3438
overwrite = _fix_overwrite_type(overwrite)
3439
self._update_revisions(stop_revision,
3440
overwrite=("history" in overwrite),
3527
self._update_revisions(stop_revision, overwrite=overwrite,
3442
3529
# TODO: The old revid should be specified when merging tags,
3443
3530
# so a tags implementation that versions tags can only
3444
3531
# pull in the most recent changes. -- JRV20090506
3445
result.tag_updates, result.tag_conflicts = (
3446
self.source.tags.merge_to(self.target.tags,
3447
"tags" in overwrite,
3448
ignore_master=not merge_tags_to_master))
3532
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3533
overwrite, ignore_master=not merge_tags_to_master)
3449
3534
result.new_revno, result.new_revid = self.target.last_revision_info()
3450
3535
if _hook_master:
3451
3536
result.master_branch = _hook_master