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
18
21
from cStringIO import StringIO
21
23
from bzrlib.lazy_import import lazy_import
22
24
lazy_import(globals(), """
23
from itertools import chain
24
26
from bzrlib import (
27
config as _mod_config,
36
revision as _mod_revision,
44
from bzrlib.config import BranchConfig, TransportConfig
45
from bzrlib.tag import (
31
config as _mod_config,
40
revision as _mod_revision,
49
from bzrlib.i18n import gettext, ngettext
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
53
# is guaranteed to be registered.
51
56
from bzrlib import (
54
60
from bzrlib.decorators import (
104
105
self._last_revision_info_cache = None
105
106
self._master_branch_cache = None
106
107
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
108
109
hooks = Branch.hooks['open']
109
110
for hook in hooks:
112
def _open_hook(self):
113
def _open_hook(self, possible_transports):
113
114
"""Called by init to allow simpler extension of the base class."""
115
def _activate_fallback_location(self, url):
116
def _activate_fallback_location(self, url, possible_transports):
116
117
"""Activate the branch/repository from url as a fallback repository."""
117
118
for existing_fallback_repo in self.repository._fallback_repositories:
118
119
if existing_fallback_repo.user_url == url:
119
120
# This fallback is already configured. This probably only
120
# happens because BzrDir.sprout is a horrible mess. To avoid
121
# happens because ControlDir.sprout is a horrible mess. To avoid
121
122
# confusing _unstack we don't add this a second time.
122
123
mutter('duplicate activation of fallback %r on %r', url, self)
124
repo = self._get_fallback_repository(url)
125
repo = self._get_fallback_repository(url, possible_transports)
125
126
if repo.has_same_location(self.repository):
126
127
raise errors.UnstackableLocationError(self.user_url, url)
127
128
self.repository.add_fallback_repository(repo)
181
182
For instance, if the branch is at URL/.bzr/branch,
182
183
Branch.open(URL) -> a Branch instance.
184
control = bzrdir.BzrDir.open(base, _unsupported,
185
possible_transports=possible_transports)
186
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)
189
def open_from_transport(transport, name=None, _unsupported=False):
191
def open_from_transport(transport, name=None, _unsupported=False,
192
possible_transports=None):
190
193
"""Open the branch rooted at transport"""
191
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
192
return control.open_branch(name=name, unsupported=_unsupported)
194
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
195
return control.open_branch(name=name, unsupported=_unsupported,
196
possible_transports=possible_transports)
195
199
def open_containing(url, possible_transports=None):
238
253
raise NotImplementedError(self._get_config)
240
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):
241
273
"""Get the repository we fallback to at url."""
242
274
url = urlutils.join(self.base, url)
243
a_branch = Branch.open(url,
244
possible_transports=[self.bzrdir.root_transport])
275
a_branch = Branch.open(url, possible_transports=possible_transports)
245
276
return a_branch.repository
512
545
rev_iter = iter(merge_sorted_revisions)
513
546
if start_revision_id is not None:
514
547
for node in rev_iter:
515
rev_id = node.key[-1]
516
549
if rev_id != start_revision_id:
519
552
# The decision to include the start or not
520
553
# depends on the stop_rule if a stop is provided
521
554
# so pop this node back into the iterator
522
rev_iter = chain(iter([node]), rev_iter)
555
rev_iter = itertools.chain(iter([node]), rev_iter)
524
557
if stop_revision_id is None:
525
558
# Yield everything
526
559
for node in rev_iter:
527
rev_id = node.key[-1]
528
561
yield (rev_id, node.merge_depth, node.revno,
529
562
node.end_of_merge)
530
563
elif stop_rule == 'exclude':
531
564
for node in rev_iter:
532
rev_id = node.key[-1]
533
566
if rev_id == stop_revision_id:
535
568
yield (rev_id, node.merge_depth, node.revno,
536
569
node.end_of_merge)
537
570
elif stop_rule == 'include':
538
571
for node in rev_iter:
539
rev_id = node.key[-1]
540
573
yield (rev_id, node.merge_depth, node.revno,
541
574
node.end_of_merge)
542
575
if rev_id == stop_revision_id:
669
704
raise errors.UnsupportedOperation(self.get_reference_info, self)
671
706
@needs_write_lock
672
def fetch(self, from_branch, last_revision=None, fetch_spec=None):
707
def fetch(self, from_branch, last_revision=None, limit=None):
673
708
"""Copy revisions from from_branch into this branch.
675
710
:param from_branch: Where to copy from.
676
711
:param last_revision: What revision to stop at (None for at the end
678
:param fetch_spec: If specified, a SearchResult or
679
PendingAncestryResult that describes which revisions to copy. This
680
allows copying multiple heads at once. Mutually exclusive with
713
:param limit: Optional rough limit of revisions to fetch
684
return InterBranch.get(from_branch, self).fetch(last_revision,
716
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
687
718
def get_bound_location(self):
688
719
"""Return the URL of the branch we are bound to.
751
784
"""Print `file` to stdout."""
752
785
raise NotImplementedError(self.print_file)
754
def set_revision_history(self, rev_history):
755
raise NotImplementedError(self.set_revision_history)
788
def set_last_revision_info(self, revno, revision_id):
789
"""Set the last revision of this branch.
791
The caller is responsible for checking that the revno is correct
792
for this revision id.
794
It may be possible to set the branch last revision to an id not
795
present in the repository. However, branches can also be
796
configured to check constraints on history, in which case this may not
799
raise NotImplementedError(self.set_last_revision_info)
802
def generate_revision_history(self, revision_id, last_rev=None,
804
"""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)
816
self.set_last_revision_info(revno, revision_id)
757
818
@needs_write_lock
758
819
def set_parent(self, url):
988
1046
:return: A tuple (revno, revision_id).
990
1048
if self._last_revision_info_cache is None:
991
self._last_revision_info_cache = self._last_revision_info()
1049
self._last_revision_info_cache = self._read_last_revision_info()
992
1050
return self._last_revision_info_cache
994
def _last_revision_info(self):
995
rh = self.revision_history()
998
return (revno, rh[-1])
1000
return (0, _mod_revision.NULL_REVISION)
1002
def update_revisions(self, other, stop_revision=None, overwrite=False,
1003
graph=None, fetch_tags=True):
1004
"""Pull in new perfect-fit revisions.
1006
:param other: Another Branch to pull from
1007
:param stop_revision: Updated until the given revision
1008
:param overwrite: Always set the branch pointer, rather than checking
1009
to see if it is a proper descendant.
1010
:param graph: A Graph object that can be used to query history
1011
information. This can be None.
1012
:param fetch_tags: Flag that specifies if tags from other should be
1016
return InterBranch.get(other, self).update_revisions(stop_revision,
1017
overwrite, graph, fetch_tags=fetch_tags)
1019
@deprecated_method(deprecated_in((2, 4, 0)))
1020
def import_last_revision_info(self, source_repo, revno, revid):
1021
"""Set the last revision info, importing from another repo if necessary.
1023
:param source_repo: Source repository to optionally fetch from
1024
:param revno: Revision number of the new tip
1025
:param revid: Revision id of the new tip
1027
if not self.repository.has_same_location(source_repo):
1028
self.repository.fetch(source_repo, revision_id=revid)
1029
self.set_last_revision_info(revno, revid)
1052
def _read_last_revision_info(self):
1053
raise NotImplementedError(self._read_last_revision_info)
1031
1055
def import_last_revision_info_and_tags(self, source, revno, revid,
1093
1110
stop_revision=stop_revision,
1094
1111
possible_transports=possible_transports, *args, **kwargs)
1096
def push(self, target, overwrite=False, stop_revision=None, *args,
1113
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1098
1115
"""Mirror this branch into target.
1100
1117
This branch is considered to be 'local', having low latency.
1102
1119
return InterBranch.get(self, target).push(overwrite, stop_revision,
1105
def lossy_push(self, target, stop_revision=None):
1106
"""Push deltas into another branch.
1108
:note: This does not, like push, retain the revision ids from
1109
the source branch and will, rather than adding bzr-specific
1110
metadata, push only those semantics of the revision that can be
1111
natively represented by this branch' VCS.
1113
:param target: Target branch
1114
:param stop_revision: Revision to push, defaults to last revision.
1115
:return: BranchPushResult with an extra member revidmap:
1116
A dictionary mapping revision ids from the target branch
1117
to new revision ids in the target branch, for each
1118
revision that was pushed.
1120
inter = InterBranch.get(self, target)
1121
lossy_push = getattr(inter, "lossy_push", None)
1122
if lossy_push is None:
1123
raise errors.LossyPushToSameVCS(self, target)
1124
return lossy_push(stop_revision)
1120
lossy, *args, **kwargs)
1126
1122
def basis_tree(self):
1127
1123
"""Return `Tree` object for last revision."""
1152
1148
def _set_config_location(self, name, url, config=None,
1153
1149
make_relative=False):
1154
1150
if config is None:
1155
config = self.get_config()
1151
config = self.get_config_stack()
1156
1152
if url is None:
1158
1154
elif make_relative:
1159
1155
url = urlutils.relative_url(self.base, url)
1160
config.set_user_option(name, url, warn_masked=True)
1156
config.set(name, url)
1162
1158
def _get_config_location(self, name, config=None):
1163
1159
if config is None:
1164
config = self.get_config()
1165
location = config.get_user_option(name)
1160
config = self.get_config_stack()
1161
location = config.get(name)
1166
1162
if location == '':
1167
1163
location = None
1168
1164
return location
1170
1166
def get_child_submit_format(self):
1171
1167
"""Return the preferred format of submissions to this branch."""
1172
return self.get_config().get_user_option("child_submit_format")
1168
return self.get_config_stack().get('child_submit_format')
1174
1170
def get_submit_branch(self):
1175
1171
"""Return the submit location of the branch.
1437
1435
t = transport.get_transport(to_location)
1438
1436
t.ensure_base()
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
1439
1455
if lightweight:
1440
format = self._get_checkout_format()
1441
checkout = format.initialize_on_transport(t)
1442
from_branch = BranchReferenceFormat().initialize(checkout,
1456
from_branch = checkout.set_branch_reference(target_branch=self)
1445
format = self._get_checkout_format()
1446
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1447
to_location, force_new_tree=False, format=format)
1448
checkout = checkout_branch.bzrdir
1458
policy = checkout.determine_repository_policy()
1459
repo = policy.acquire_repository()[0]
1460
checkout_branch = checkout.create_branch()
1449
1461
checkout_branch.bind(self)
1450
1462
# pull up to the specified revision_id to set the initial
1451
1463
# branch tip correctly, and seed it with history.
1452
1464
checkout_branch.pull(self, stop_revision=revision_id)
1454
1466
tree = checkout.create_workingtree(revision_id,
1455
1467
from_branch=from_branch,
1456
1468
accelerator_tree=accelerator_tree,
1574
1589
object will be created every time regardless.
1577
can_set_append_revisions_only = True
1579
1592
def __eq__(self, other):
1580
1593
return self.__class__ is other.__class__
1582
1595
def __ne__(self, other):
1583
1596
return not (self == other)
1586
def find_format(klass, a_bzrdir, name=None):
1587
"""Return the format for the branch object in a_bzrdir."""
1589
transport = a_bzrdir.get_branch_transport(None, name=name)
1590
format_string = transport.get_bytes("format")
1591
return format_registry.get(format_string)
1592
except errors.NoSuchFile:
1593
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1595
raise errors.UnknownFormatError(format=format_string, kind='branch')
1598
@deprecated_method(deprecated_in((2, 4, 0)))
1599
def get_default_format(klass):
1600
"""Return the current default format."""
1601
return format_registry.get_default()
1604
@deprecated_method(deprecated_in((2, 4, 0)))
1605
def get_formats(klass):
1606
"""Get all the known formats.
1608
Warning: This triggers a load of all lazy registered formats: do not
1609
use except when that is desireed.
1611
return format_registry._get_all()
1613
def get_reference(self, a_bzrdir, name=None):
1614
"""Get the target reference of the branch in a_bzrdir.
1598
def get_reference(self, controldir, name=None):
1599
"""Get the target reference of the branch in controldir.
1616
1601
format probing must have been completed before calling
1617
1602
this method - it is assumed that the format of the branch
1618
in a_bzrdir is correct.
1603
in controldir is correct.
1620
:param a_bzrdir: The bzrdir to get the branch data from.
1605
:param controldir: The controldir to get the branch data from.
1621
1606
:param name: Name of the colocated branch to fetch
1622
1607
:return: None if the branch is not a reference branch.
1627
def set_reference(self, a_bzrdir, name, to_branch):
1628
"""Set the target reference of the branch in a_bzrdir.
1612
def set_reference(self, controldir, name, to_branch):
1613
"""Set the target reference of the branch in controldir.
1630
1615
format probing must have been completed before calling
1631
1616
this method - it is assumed that the format of the branch
1632
in a_bzrdir is correct.
1617
in controldir is correct.
1634
:param a_bzrdir: The bzrdir to set the branch reference for.
1619
:param controldir: The controldir to set the branch reference for.
1635
1620
:param name: Name of colocated branch to set, None for default
1636
1621
:param to_branch: branch that the checkout is to reference
1638
1623
raise NotImplementedError(self.set_reference)
1640
def get_format_string(self):
1641
"""Return the ASCII format string that identifies this format."""
1642
raise NotImplementedError(self.get_format_string)
1644
1625
def get_format_description(self):
1645
1626
"""Return the short format description for this format."""
1646
1627
raise NotImplementedError(self.get_format_description)
1648
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1629
def _run_post_branch_init_hooks(self, controldir, name, branch):
1649
1630
hooks = Branch.hooks['post_branch_init']
1652
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1633
params = BranchInitHookParams(self, controldir, name, branch)
1653
1634
for hook in hooks:
1656
def initialize(self, a_bzrdir, name=None, repository=None):
1657
"""Create a branch of this format in a_bzrdir.
1637
def initialize(self, controldir, name=None, repository=None,
1638
append_revisions_only=None):
1639
"""Create a branch of this format in controldir.
1659
1641
:param name: Name of the colocated branch to create.
1661
1643
raise NotImplementedError(self.initialize)
1982
1946
self.revision_id)
1985
class BranchFormatMetadir(BranchFormat):
1986
"""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)
1988
1970
def _branch_class(self):
1989
1971
"""What class to instantiate on open calls."""
1990
1972
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.
1992
1982
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1993
1983
repository=None):
1994
"""Initialize a branch in a bzrdir, with specified files
1984
"""Initialize a branch in a control dir, with specified files
1996
1986
:param a_bzrdir: The bzrdir to initialize the branch in
1997
1987
:param utf8_files: The files to create as a list of
2059
2048
def supports_leaving_lock(self):
2063
class BzrBranchFormat5(BranchFormatMetadir):
2064
"""Bzr branch format 5.
2067
- a revision-history file.
2069
- a lock dir guarding the branch itself
2070
- all of this stored in a branch/ subdirectory
2071
- works with shared repositories.
2073
This format is new in bzr 0.8.
2076
def _branch_class(self):
2079
def get_format_string(self):
2080
"""See BranchFormat.get_format_string()."""
2081
return "Bazaar-NG branch format 5\n"
2083
def get_format_description(self):
2084
"""See BranchFormat.get_format_description()."""
2085
return "Branch format 5"
2087
def initialize(self, a_bzrdir, name=None, repository=None):
2088
"""Create a branch of this format in a_bzrdir."""
2089
utf8_files = [('revision-history', ''),
2090
('branch-name', ''),
2092
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2094
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)
2098
2060
class BzrBranchFormat6(BranchFormatMetadir):
2117
2080
"""See BranchFormat.get_format_description()."""
2118
2081
return "Branch format 6"
2120
def initialize(self, a_bzrdir, name=None, repository=None):
2083
def initialize(self, a_bzrdir, name=None, repository=None,
2084
append_revisions_only=None):
2121
2085
"""Create a branch of this format in a_bzrdir."""
2122
2086
utf8_files = [('last-revision', '0 null:\n'),
2123
('branch.conf', ''),
2088
self._get_initial_config(append_revisions_only)),
2126
2091
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2128
2093
def make_tags(self, branch):
2129
2094
"""See bzrlib.branch.BranchFormat.make_tags()."""
2130
return BasicTags(branch)
2095
return _mod_tag.BasicTags(branch)
2132
2097
def supports_set_append_revisions_only(self):
2240
2212
location = transport.put_bytes('location', to_branch.base)
2242
2214
def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
repository=None, append_revisions_only=None):
2244
2216
"""Create a branch of this format in a_bzrdir."""
2245
2217
if target_branch is None:
2246
2218
# this format does not implement branch itself, thus the implicit
2247
2219
# creation contract must see it as uninitializable
2248
2220
raise errors.UninitializableFormat(self)
2249
2221
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()
2250
2226
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2251
2227
branch_transport.put_bytes('location',
2252
target_branch.bzrdir.user_url)
2253
branch_transport.put_bytes('format', self.get_format_string())
2255
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,
2256
2231
possible_transports=[target_branch.bzrdir.root_transport])
2257
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2261
super(BranchReferenceFormat, self).__init__()
2262
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2263
self._matchingbzrdir.set_branch_format(self)
2265
2235
def _make_reference_clone_function(format, a_branch):
2266
2236
"""Create a clone() routine for a branch dynamically."""
2267
2237
def clone(to_bzrdir, revision_id=None,
2290
2260
:param possible_transports: An optional reusable transports list.
2263
name = a_bzrdir._get_selected_branch()
2293
format = BranchFormat.find_format(a_bzrdir, name=name)
2265
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2294
2266
if format.__class__ != self.__class__:
2295
2267
raise AssertionError("wrong format %r found for %r" %
2296
2268
(format, self))
2297
2269
if location is None:
2298
2270
location = self.get_reference(a_bzrdir, name)
2299
real_bzrdir = bzrdir.BzrDir.open(
2271
real_bzrdir = controldir.ControlDir.open(
2300
2272
location, possible_transports=possible_transports)
2301
result = real_bzrdir.open_branch(name=name,
2302
ignore_fallbacks=ignore_fallbacks)
2273
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2274
possible_transports=possible_transports)
2303
2275
# this changes the behaviour of result.clone to create a new reference
2304
2276
# rather than a copy of the content of the branch.
2305
2277
# I did not use a proxy object because that needs much more extensive
2387
2359
def __init__(self, _format=None,
2388
2360
_control_files=None, a_bzrdir=None, name=None,
2389
_repository=None, ignore_fallbacks=False):
2361
_repository=None, ignore_fallbacks=False,
2362
possible_transports=None):
2390
2363
"""Create new branch object at a particular location."""
2391
2364
if a_bzrdir is None:
2392
2365
raise ValueError('a_bzrdir must be supplied')
2394
self.bzrdir = a_bzrdir
2395
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
2396
2374
self.name = name
2397
# XXX: We should be able to just do
2398
# self.base = self.bzrdir.root_transport.base
2399
# but this does not quite work yet -- mbp 20080522
2400
2375
self._format = _format
2401
2376
if _control_files is None:
2402
2377
raise ValueError('BzrBranch _control_files is None')
2403
2378
self.control_files = _control_files
2404
2379
self._transport = _control_files._transport
2405
2380
self.repository = _repository
2406
Branch.__init__(self)
2381
self.conf_store = None
2382
Branch.__init__(self, possible_transports)
2408
2384
def __str__(self):
2409
if self.name is None:
2410
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2412
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2385
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2415
2387
__repr__ = __str__
2421
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
2423
2399
def _get_config(self):
2424
return TransportConfig(self._transport, 'branch.conf')
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)
2426
2446
def is_locked(self):
2427
2447
return self.control_files.is_locked()
2502
2514
"""See Branch.print_file."""
2503
2515
return self.repository.print_file(file, revision_id)
2505
def _write_revision_history(self, history):
2506
"""Factored out of set_revision_history.
2508
This performs the actual writing to disk.
2509
It is intended to be called by BzrBranch5.set_revision_history."""
2510
self._transport.put_bytes(
2511
'revision-history', '\n'.join(history),
2512
mode=self.bzrdir._get_file_mode())
2514
@deprecated_method(deprecated_in((2, 4, 0)))
2515
def set_revision_history(self, rev_history):
2516
"""See Branch.set_revision_history."""
2517
self._set_revision_history(rev_history)
2520
def _set_revision_history(self, rev_history):
2521
if 'evil' in debug.debug_flags:
2522
mutter_callsite(3, "set_revision_history scales with history.")
2523
check_not_reserved_id = _mod_revision.check_not_reserved_id
2524
for rev_id in rev_history:
2525
check_not_reserved_id(rev_id)
2526
if Branch.hooks['post_change_branch_tip']:
2527
# Don't calculate the last_revision_info() if there are no hooks
2529
old_revno, old_revid = self.last_revision_info()
2530
if len(rev_history) == 0:
2531
revid = _mod_revision.NULL_REVISION
2533
revid = rev_history[-1]
2534
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2535
self._write_revision_history(rev_history)
2536
self._clear_cached_state()
2537
self._cache_revision_history(rev_history)
2538
for hook in Branch.hooks['set_rh']:
2539
hook(self, rev_history)
2540
if Branch.hooks['post_change_branch_tip']:
2541
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2543
def _synchronize_history(self, destination, revision_id):
2544
"""Synchronize last revision and revision history between branches.
2546
This version is most efficient when the destination is also a
2547
BzrBranch5, but works for BzrBranch6 as long as the revision
2548
history is the true lefthand parent history, and all of the revisions
2549
are in the destination's repository. If not, set_revision_history
2552
:param destination: The branch to copy the history into
2553
:param revision_id: The revision-id to truncate history at. May
2554
be None to copy complete history.
2556
if not isinstance(destination._format, BzrBranchFormat5):
2557
super(BzrBranch, self)._synchronize_history(
2558
destination, revision_id)
2560
if revision_id == _mod_revision.NULL_REVISION:
2563
new_history = self.revision_history()
2564
if revision_id is not None and new_history != []:
2566
new_history = new_history[:new_history.index(revision_id) + 1]
2568
rev = self.repository.get_revision(revision_id)
2569
new_history = rev.get_history(self.repository)[1:]
2570
destination._set_revision_history(new_history)
2572
2517
@needs_write_lock
2573
2518
def set_last_revision_info(self, revno, revision_id):
2574
"""Set the last revision of this branch.
2576
The caller is responsible for checking that the revno is correct
2577
for this revision id.
2579
It may be possible to set the branch last revision to an id not
2580
present in the repository. However, branches can also be
2581
configured to check constraints on history, in which case this may not
2584
2519
if not revision_id or not isinstance(revision_id, basestring):
2585
2520
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2586
# this old format stores the full history, but this api doesn't
2587
# provide it, so we must generate, and might as well check it's
2589
history = self._lefthand_history(revision_id)
2590
if len(history) != revno:
2591
raise AssertionError('%d != %d' % (len(history), revno))
2592
self._set_revision_history(history)
2594
def _gen_revision_history(self):
2595
history = self._transport.get_bytes('revision-history').split('\n')
2596
if history[-1:] == ['']:
2597
# There shouldn't be a trailing newline, but just in case.
2602
def generate_revision_history(self, revision_id, last_rev=None,
2604
"""Create a new revision history that will finish with revision_id.
2606
:param revision_id: the new tip to use.
2607
:param last_rev: The previous last_revision. If not None, then this
2608
must be a ancestory of revision_id, or DivergedBranches is raised.
2609
:param other_branch: The other branch that DivergedBranches should
2610
raise with respect to.
2612
self._set_revision_history(self._lefthand_history(revision_id,
2613
last_rev, other_branch))
2521
revision_id = _mod_revision.ensure_null(revision_id)
2522
old_revno, old_revid = self.last_revision_info()
2523
if self.get_append_revisions_only():
2524
self._check_history_violation(revision_id)
2525
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2526
self._write_last_revision_info(revno, revision_id)
2527
self._clear_cached_state()
2528
self._last_revision_info_cache = revno, revision_id
2529
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2615
2531
def basis_tree(self):
2616
2532
"""See Branch.basis_tree."""
2628
def _basic_push(self, target, overwrite, stop_revision):
2629
"""Basic implementation of push without bound branches or hooks.
2631
Must be called with source read locked and target write locked.
2633
result = BranchPushResult()
2634
result.source_branch = self
2635
result.target_branch = target
2636
result.old_revno, result.old_revid = target.last_revision_info()
2637
self.update_references(target)
2638
if result.old_revid != stop_revision:
2639
# We assume that during 'push' this repository is closer than
2641
graph = self.repository.get_graph(target.repository)
2642
target.update_revisions(self, stop_revision,
2643
overwrite=overwrite, graph=graph)
2644
if self._push_should_merge_tags():
2645
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2646
result.new_revno, result.new_revid = target.last_revision_info()
2649
2544
def get_stacked_on_url(self):
2650
2545
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2662
2557
self._transport.put_bytes('parent', url + '\n',
2663
2558
mode=self.bzrdir._get_file_mode())
2666
class BzrBranch5(BzrBranch):
2667
"""A format 5 branch. This supports new features over plain branches.
2669
It has support for a master_branch which is the data for bound branches.
2562
"""If bound, unbind"""
2563
return self.set_bound_location(None)
2566
def bind(self, other):
2567
"""Bind this branch to the branch other.
2569
This does not push or pull data between the branches, though it does
2570
check for divergence to raise an error when the branches are not
2571
either the same, or one a prefix of the other. That behaviour may not
2572
be useful, so that check may be removed in future.
2574
:param other: The branch to bind to
2577
# TODO: jam 20051230 Consider checking if the target is bound
2578
# It is debatable whether you should be able to bind to
2579
# a branch which is itself bound.
2580
# Committing is obviously forbidden,
2581
# but binding itself may not be.
2582
# Since we *have* to check at commit time, we don't
2583
# *need* to check here
2585
# we want to raise diverged if:
2586
# last_rev is not in the other_last_rev history, AND
2587
# other_last_rev is not in our history, and do it without pulling
2589
self.set_bound_location(other.base)
2672
2591
def get_bound_location(self):
2717
2636
@needs_write_lock
2718
def bind(self, other):
2719
"""Bind this branch to the branch other.
2721
This does not push or pull data between the branches, though it does
2722
check for divergence to raise an error when the branches are not
2723
either the same, or one a prefix of the other. That behaviour may not
2724
be useful, so that check may be removed in future.
2726
:param other: The branch to bind to
2729
# TODO: jam 20051230 Consider checking if the target is bound
2730
# It is debatable whether you should be able to bind to
2731
# a branch which is itself bound.
2732
# Committing is obviously forbidden,
2733
# but binding itself may not be.
2734
# Since we *have* to check at commit time, we don't
2735
# *need* to check here
2737
# we want to raise diverged if:
2738
# last_rev is not in the other_last_rev history, AND
2739
# other_last_rev is not in our history, and do it without pulling
2741
self.set_bound_location(other.base)
2745
"""If bound, unbind"""
2746
return self.set_bound_location(None)
2749
2637
def update(self, possible_transports=None):
2750
2638
"""Synchronise this branch with the master branch if any.
2766
class BzrBranch8(BzrBranch5):
2653
def _read_last_revision_info(self):
2654
revision_string = self._transport.get_bytes('last-revision')
2655
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2656
revision_id = cache_utf8.get_cached_utf8(revision_id)
2658
return revno, revision_id
2660
def _write_last_revision_info(self, revno, revision_id):
2661
"""Simply write out the revision id, with no checks.
2663
Use set_last_revision_info to perform this safely.
2665
Does not update the revision_history cache.
2667
revision_id = _mod_revision.ensure_null(revision_id)
2668
out_string = '%d %s\n' % (revno, revision_id)
2669
self._transport.put_bytes('last-revision', out_string,
2670
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
2679
self._format._update_feature_flags(updated_flags)
2680
self.control_transport.put_bytes('format', self._format.as_string())
2683
class BzrBranch8(BzrBranch):
2767
2684
"""A branch that stores tree-reference locations."""
2769
def _open_hook(self):
2686
def _open_hook(self, possible_transports=None):
2770
2687
if self._ignore_fallbacks:
2689
if possible_transports is None:
2690
possible_transports = [self.bzrdir.root_transport]
2773
2692
url = self.get_stacked_on_url()
2774
2693
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2795
2715
self._last_revision_info_cache = None
2796
2716
self._reference_info = None
2798
def _last_revision_info(self):
2799
revision_string = self._transport.get_bytes('last-revision')
2800
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2801
revision_id = cache_utf8.get_cached_utf8(revision_id)
2803
return revno, revision_id
2805
def _write_last_revision_info(self, revno, revision_id):
2806
"""Simply write out the revision id, with no checks.
2808
Use set_last_revision_info to perform this safely.
2810
Does not update the revision_history cache.
2811
Intended to be called by set_last_revision_info and
2812
_write_revision_history.
2814
revision_id = _mod_revision.ensure_null(revision_id)
2815
out_string = '%d %s\n' % (revno, revision_id)
2816
self._transport.put_bytes('last-revision', out_string,
2817
mode=self.bzrdir._get_file_mode())
2820
def set_last_revision_info(self, revno, revision_id):
2821
if not revision_id or not isinstance(revision_id, basestring):
2822
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2823
old_revno, old_revid = self.last_revision_info()
2824
if self._get_append_revisions_only():
2825
self._check_history_violation(revision_id)
2826
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2827
self._write_last_revision_info(revno, revision_id)
2828
self._clear_cached_state()
2829
self._last_revision_info_cache = revno, revision_id
2830
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2832
def _synchronize_history(self, destination, revision_id):
2833
"""Synchronize last revision and revision history between branches.
2835
:see: Branch._synchronize_history
2837
# XXX: The base Branch has a fast implementation of this method based
2838
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2839
# that uses set_revision_history. This class inherits from BzrBranch5,
2840
# but wants the fast implementation, so it calls
2841
# Branch._synchronize_history directly.
2842
Branch._synchronize_history(self, destination, revision_id)
2844
2718
def _check_history_violation(self, revision_id):
2845
last_revision = _mod_revision.ensure_null(self.last_revision())
2719
current_revid = self.last_revision()
2720
last_revision = _mod_revision.ensure_null(current_revid)
2846
2721
if _mod_revision.is_null(last_revision):
2848
if last_revision not in self._lefthand_history(revision_id):
2849
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2723
graph = self.repository.get_graph()
2724
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2725
if lh_ancestor == current_revid:
2727
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2851
2729
def _gen_revision_history(self):
2852
2730
"""Generate the revision history from last revision
2964
2826
"""See Branch.set_push_location."""
2965
2827
self._master_branch_cache = None
2967
config = self.get_config()
2829
conf = self.get_config_stack()
2968
2830
if location is None:
2969
if config.get_user_option('bound') != 'True':
2831
if not conf.get('bound'):
2972
config.set_user_option('bound', 'False', warn_masked=True)
2834
conf.set('bound', 'False')
2975
2837
self._set_config_location('bound_location', location,
2977
config.set_user_option('bound', 'True', warn_masked=True)
2839
conf.set('bound', 'True')
2980
2842
def _get_bound_location(self, bound):
2981
2843
"""Return the bound location in the config file.
2983
2845
Return None if the bound parameter does not match"""
2984
config = self.get_config()
2985
config_bound = (config.get_user_option('bound') == 'True')
2986
if config_bound != bound:
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
2988
return self._get_config_location('bound_location', config=config)
2849
return self._get_config_location('bound_location', config=conf)
2990
2851
def get_bound_location(self):
2991
"""See Branch.set_push_location."""
2852
"""See Branch.get_bound_location."""
2992
2853
return self._get_bound_location(True)
2994
2855
def get_old_bound_location(self):
2999
2860
# you can always ask for the URL; but you might not be able to use it
3000
2861
# if the repo can't support stacking.
3001
2862
## self._check_stackable_repo()
3002
stacked_url = self._get_config_location('stacked_on_location')
2863
# stacked_on_location is only ever defined in branch.conf, so don't
2864
# waste effort reading the whole stack of config files.
2865
conf = _mod_config.BranchOnlyStack(self)
2866
stacked_url = self._get_config_location('stacked_on_location',
3003
2868
if stacked_url is None:
3004
2869
raise errors.NotStacked(self)
3007
def _get_append_revisions_only(self):
3008
return self.get_config(
3009
).get_user_option_as_bool('append_revisions_only')
3012
def generate_revision_history(self, revision_id, last_rev=None,
3014
"""See BzrBranch5.generate_revision_history"""
3015
history = self._lefthand_history(revision_id, last_rev, other_branch)
3016
revno = len(history)
3017
self.set_last_revision_info(revno, revision_id)
2870
return stacked_url.encode('utf-8')
3019
2872
@needs_read_lock
3020
2873
def get_rev_id(self, revno, history=None):
3107
2962
:ivar local_branch: target branch if there is a Master, else None
3108
2963
:ivar target_branch: Target/destination branch object. (write locked)
3109
2964
: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
3112
@deprecated_method(deprecated_in((2, 3, 0)))
3114
"""Return the relative change in revno.
3116
:deprecated: Use `new_revno` and `old_revno` instead.
3118
return self.new_revno - self.old_revno
3120
2968
def report(self, to_file):
2969
tag_conflicts = getattr(self, "tag_conflicts", None)
2970
tag_updates = getattr(self, "tag_updates", None)
3121
2971
if not is_quiet():
3122
if self.old_revid == self.new_revid:
3123
to_file.write('No revisions to pull.\n')
2972
if self.old_revid != self.new_revid:
3125
2973
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')
3126
2981
self._show_tag_conficts(to_file)
3145
3000
target, otherwise it will be None.
3148
@deprecated_method(deprecated_in((2, 3, 0)))
3150
"""Return the relative change in revno.
3152
:deprecated: Use `new_revno` and `old_revno` instead.
3154
return self.new_revno - self.old_revno
3156
3003
def report(self, to_file):
3157
"""Write a human-readable description of the result."""
3158
if self.old_revid == self.new_revid:
3159
note('No new revisions to push.')
3161
note('Pushed up to revision %d.' % self.new_revno)
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.'))
3162
3020
self._show_tag_conficts(to_file)
3195
3053
# Copy source data into target
3196
3054
new_branch._write_last_revision_info(*branch.last_revision_info())
3197
new_branch.set_parent(branch.get_parent())
3198
new_branch.set_bound_location(branch.get_bound_location())
3199
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())
3201
3063
# New branch has no tags by default
3202
3064
new_branch.tags._set_tag_dict({})
3204
3066
# Copying done; now update target format
3205
3067
new_branch._transport.put_bytes('format',
3206
format.get_format_string(),
3207
3069
mode=new_branch.bzrdir._get_file_mode())
3209
3071
# Clean up old files
3210
3072
new_branch._transport.delete('revision-history')
3212
branch.set_parent(None)
3213
except errors.NoSuchFile:
3215
branch.set_bound_location(None)
3076
branch.set_parent(None)
3077
except errors.NoSuchFile:
3079
branch.set_bound_location(None)
3218
3084
class Converter6to7(object):
3222
3088
format = BzrBranchFormat7()
3223
3089
branch._set_config_location('stacked_on_location', '')
3224
3090
# update target format
3225
branch._transport.put_bytes('format', format.get_format_string())
3091
branch._transport.put_bytes('format', format.as_string())
3228
3094
class Converter7to8(object):
3229
"""Perform an in-place upgrade of format 6 to format 7"""
3095
"""Perform an in-place upgrade of format 7 to format 8"""
3231
3097
def convert(self, branch):
3232
3098
format = BzrBranchFormat8()
3233
3099
branch._transport.put_bytes('references', '')
3234
3100
# update target format
3235
branch._transport.put_bytes('format', format.get_format_string())
3238
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3239
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3242
_run_with_write_locked_target will attempt to release the lock it acquires.
3244
If an exception is raised by callable, then that exception *will* be
3245
propagated, even if the unlock attempt raises its own error. Thus
3246
_run_with_write_locked_target should be preferred to simply doing::
3250
return callable(*args, **kwargs)
3255
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3256
# should share code?
3259
result = callable(*args, **kwargs)
3261
exc_info = sys.exc_info()
3265
raise exc_info[0], exc_info[1], exc_info[2]
3101
branch._transport.put_bytes('format', format.as_string())
3271
3104
class InterBranch(InterObject):
3301
3134
raise NotImplementedError(self.pull)
3303
3136
@needs_write_lock
3304
def update_revisions(self, stop_revision=None, overwrite=False,
3305
graph=None, fetch_tags=True):
3306
"""Pull in new perfect-fit revisions.
3308
:param stop_revision: Updated until the given revision
3309
:param overwrite: Always set the branch pointer, rather than checking
3310
to see if it is a proper descendant.
3311
:param graph: A Graph object that can be used to query history
3312
information. This can be None.
3313
:param fetch_tags: Flag that specifies if tags from source should be
3317
raise NotImplementedError(self.update_revisions)
3320
def push(self, overwrite=False, stop_revision=None,
3137
def push(self, overwrite=False, stop_revision=None, lossy=False,
3321
3138
_override_hook_source_branch=None):
3322
3139
"""Mirror the source branch into the target branch.
3383
3209
self.source.tags.merge_to(self.target.tags)
3385
3211
@needs_write_lock
3386
def fetch(self, stop_revision=None, fetch_spec=None):
3387
if fetch_spec is not None and stop_revision is not None:
3388
raise AssertionError(
3389
"fetch_spec and last_revision are mutually exclusive.")
3212
def fetch(self, stop_revision=None, limit=None):
3390
3213
if self.target.base == self.source.base:
3392
3215
self.source.lock_read()
3394
if stop_revision is None and fetch_spec is None:
3395
stop_revision = self.source.last_revision()
3396
stop_revision = _mod_revision.ensure_null(stop_revision)
3217
fetch_spec_factory = fetch.FetchSpecFactory()
3218
fetch_spec_factory.source_branch = self.source
3219
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3220
fetch_spec_factory.source_repo = self.source.repository
3221
fetch_spec_factory.target_repo = self.target.repository
3222
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3223
fetch_spec_factory.limit = limit
3224
fetch_spec = fetch_spec_factory.make_fetch_spec()
3397
3225
return self.target.repository.fetch(self.source.repository,
3398
revision_id=stop_revision, fetch_spec=fetch_spec)
3226
fetch_spec=fetch_spec)
3400
3228
self.source.unlock()
3402
3230
@needs_write_lock
3403
def update_revisions(self, stop_revision=None, overwrite=False,
3404
graph=None, fetch_tags=True):
3405
"""See InterBranch.update_revisions()."""
3231
def _update_revisions(self, stop_revision=None, overwrite=False,
3406
3233
other_revno, other_last_revision = self.source.last_revision_info()
3407
3234
stop_revno = None # unknown
3408
3235
if stop_revision is None:
3483
3308
if master_branch:
3484
3309
master_branch.unlock()
3486
def push(self, overwrite=False, stop_revision=None,
3311
def push(self, overwrite=False, stop_revision=None, lossy=False,
3487
3312
_override_hook_source_branch=None):
3488
3313
"""See InterBranch.push.
3490
3315
This is the basic concrete implementation of push()
3492
:param _override_hook_source_branch: If specified, run
3493
the hooks passing this Branch as the source, rather than self.
3494
This is for use of RemoteBranch, where push is delegated to the
3495
underlying vfs-based Branch.
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
3323
raise errors.LossyPushToSameVCS(self.source, self.target)
3497
3324
# TODO: Public option to disable running hooks - should be trivial but
3499
self.source.lock_read()
3501
return _run_with_write_locked_target(
3502
self.target, self._push_with_bound_branches, overwrite,
3504
_override_hook_source_branch=_override_hook_source_branch)
3506
self.source.unlock()
3508
def _push_with_bound_branches(self, overwrite, stop_revision,
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)
3333
def _basic_push(self, overwrite, stop_revision):
3334
"""Basic implementation of push without bound branches or hooks.
3336
Must be called with source read locked and target write locked.
3338
result = BranchPushResult()
3339
result.source_branch = self.source
3340
result.target_branch = self.target
3341
result.old_revno, result.old_revid = self.target.last_revision_info()
3342
self.source.update_references(self.target)
3343
overwrite = _fix_overwrite_type(overwrite)
3344
if result.old_revid != stop_revision:
3345
# We assume that during 'push' this repository is closer than
3347
graph = self.source.repository.get_graph(self.target.repository)
3348
self._update_revisions(stop_revision,
3349
overwrite=("history" in overwrite),
3351
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))
3355
result.new_revno, result.new_revid = self.target.last_revision_info()
3358
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3509
3359
_override_hook_source_branch=None):
3510
3360
"""Push from source into target, and into target's master if any.
3523
3373
# be bound to itself? -- mbp 20070507
3524
3374
master_branch = self.target.get_master_branch()
3525
3375
master_branch.lock_write()
3527
# push into the master from the source branch.
3528
self.source._basic_push(master_branch, overwrite, stop_revision)
3529
# and push into the target branch from the source. Note that we
3530
# push from the source branch again, because it's considered the
3531
# highest bandwidth repository.
3532
result = self.source._basic_push(self.target, overwrite,
3534
result.master_branch = master_branch
3535
result.local_branch = self.target
3539
master_branch.unlock()
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
3387
master_branch = None
3541
3388
# no master branch
3542
result = self.source._basic_push(self.target, overwrite,
3389
result = self._basic_push(overwrite, stop_revision)
3544
3390
# TODO: Why set master_branch and local_branch if there's no
3545
3391
# binding? Maybe cleaner to just leave them unset? -- mbp
3547
3393
result.master_branch = self.target
3548
3394
result.local_branch = None
3552
3398
def _pull(self, overwrite=False, stop_revision=None,
3553
3399
possible_transports=None, _hook_master=None, run_hooks=True,
3589
3435
# -- JRV20090506
3590
3436
result.old_revno, result.old_revid = \
3591
3437
self.target.last_revision_info()
3592
self.target.update_revisions(self.source, stop_revision,
3593
overwrite=overwrite, graph=graph)
3438
overwrite = _fix_overwrite_type(overwrite)
3439
self._update_revisions(stop_revision,
3440
overwrite=("history" in overwrite),
3594
3442
# TODO: The old revid should be specified when merging tags,
3595
3443
# so a tags implementation that versions tags can only
3596
3444
# pull in the most recent changes. -- JRV20090506
3597
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3598
overwrite, ignore_master=not merge_tags_to_master)
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))
3599
3449
result.new_revno, result.new_revid = self.target.last_revision_info()
3600
3450
if _hook_master:
3601
3451
result.master_branch = _hook_master