485
501
* end_of_merge: When True the next node (earlier in history) is
486
502
part of a different merge.
488
with self.lock_read():
489
# Note: depth and revno values are in the context of the branch so
490
# we need the full graph to get stable numbers, regardless of the
492
if self._merge_sorted_revisions_cache is None:
493
last_revision = self.last_revision()
494
known_graph = self.repository.get_known_graph_ancestry(
496
self._merge_sorted_revisions_cache = known_graph.merge_sort(
498
filtered = self._filter_merge_sorted_revisions(
499
self._merge_sorted_revisions_cache, start_revision_id,
500
stop_revision_id, stop_rule)
501
# Make sure we don't return revisions that are not part of the
502
# start_revision_id ancestry.
503
filtered = self._filter_start_non_ancestors(filtered)
504
if direction == 'reverse':
506
if direction == 'forward':
507
return reversed(list(filtered))
509
raise ValueError('invalid direction %r' % direction)
504
# Note: depth and revno values are in the context of the branch so
505
# we need the full graph to get stable numbers, regardless of the
507
if self._merge_sorted_revisions_cache is None:
508
last_revision = self.last_revision()
509
known_graph = self.repository.get_known_graph_ancestry(
511
self._merge_sorted_revisions_cache = known_graph.merge_sort(
513
filtered = self._filter_merge_sorted_revisions(
514
self._merge_sorted_revisions_cache, start_revision_id,
515
stop_revision_id, stop_rule)
516
# Make sure we don't return revisions that are not part of the
517
# start_revision_id ancestry.
518
filtered = self._filter_start_non_ancestors(filtered)
519
if direction == 'reverse':
521
if direction == 'forward':
522
return reversed(list(filtered))
524
raise ValueError('invalid direction %r' % direction)
511
526
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
512
start_revision_id, stop_revision_id,
527
start_revision_id, stop_revision_id, stop_rule):
514
528
"""Iterate over an inclusive range of sorted revisions."""
515
529
rev_iter = iter(merge_sorted_revisions)
516
530
if start_revision_id is not None:
749
770
raise NotImplementedError(self.set_last_revision_info)
751
773
def generate_revision_history(self, revision_id, last_rev=None,
752
774
other_branch=None):
753
775
"""See Branch.generate_revision_history"""
754
with self.lock_write():
755
graph = self.repository.get_graph()
756
(last_revno, last_revid) = self.last_revision_info()
757
known_revision_ids = [
758
(last_revid, last_revno),
759
(_mod_revision.NULL_REVISION, 0),
761
if last_rev is not None:
762
if not graph.is_ancestor(last_rev, revision_id):
763
# our previous tip is not merged into stop_revision
764
raise errors.DivergedBranches(self, other_branch)
765
revno = graph.find_distance_to_null(
766
revision_id, known_revision_ids)
767
self.set_last_revision_info(revno, revision_id)
776
graph = self.repository.get_graph()
777
(last_revno, last_revid) = self.last_revision_info()
778
known_revision_ids = [
779
(last_revid, last_revno),
780
(_mod_revision.NULL_REVISION, 0),
782
if last_rev is not None:
783
if not graph.is_ancestor(last_rev, revision_id):
784
# our previous tip is not merged into stop_revision
785
raise errors.DivergedBranches(self, other_branch)
786
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
787
self.set_last_revision_info(revno, revision_id)
769
790
def set_parent(self, url):
770
791
"""See Branch.set_parent."""
771
792
# TODO: Maybe delete old location files?
795
816
if not self._format.supports_stacking():
796
raise UnstackableBranchFormat(self._format, self.user_url)
797
with self.lock_write():
798
# XXX: Changing from one fallback repository to another does not
799
# check that all the data you need is present in the new fallback.
800
# Possibly it should.
801
self._check_stackable_repo()
804
self.get_stacked_on_url()
805
except (errors.NotStacked, UnstackableBranchFormat,
806
errors.UnstackableRepositoryFormat):
810
self._activate_fallback_location(
811
url, possible_transports=[self.controldir.root_transport])
812
# write this out after the repository is stacked to avoid setting a
813
# stacked config that doesn't work.
814
self._set_config_location('stacked_on_location', url)
817
raise errors.UnstackableBranchFormat(self._format, self.user_url)
818
# XXX: Changing from one fallback repository to another does not check
819
# that all the data you need is present in the new fallback.
820
# Possibly it should.
821
self._check_stackable_repo()
824
old_url = self.get_stacked_on_url()
825
except (errors.NotStacked, errors.UnstackableBranchFormat,
826
errors.UnstackableRepositoryFormat):
830
self._activate_fallback_location(url,
831
possible_transports=[self.bzrdir.root_transport])
832
# write this out after the repository is stacked to avoid setting a
833
# stacked config that doesn't work.
834
self._set_config_location('stacked_on_location', url)
816
836
def _unstack(self):
817
837
"""Change a branch to be unstacked, copying data as needed.
819
839
Don't call this directly, use set_stacked_on_url(None).
821
with ui.ui_factory.nested_progress_bar() as pb:
841
pb = ui.ui_factory.nested_progress_bar()
822
843
pb.update(gettext("Unstacking"))
823
844
# The basic approach here is to fetch the tip of the branch,
824
845
# including all available ghosts, from the existing stacked
825
# repository into a new repository object without the fallbacks.
846
# repository into a new repository object without the fallbacks.
827
848
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
828
849
# correct for CHKMap repostiories
829
850
old_repository = self.repository
830
851
if len(old_repository._fallback_repositories) != 1:
831
raise AssertionError(
832
"can't cope with fallback repositories "
833
"of %r (fallbacks: %r)" % (
834
old_repository, old_repository._fallback_repositories))
852
raise AssertionError("can't cope with fallback repositories "
853
"of %r (fallbacks: %r)" % (old_repository,
854
old_repository._fallback_repositories))
835
855
# Open the new repository object.
836
856
# Repositories don't offer an interface to remove fallback
837
857
# repositories today; take the conceptually simpler option and just
1198
1236
revision_id: if not None, the revision history in the new branch will
1199
1237
be truncated to end with revision_id.
1201
result = to_controldir.create_branch(name=name)
1202
with self.lock_read(), result.lock_write():
1239
result = to_bzrdir.create_branch()
1203
1242
if repository_policy is not None:
1204
1243
repository_policy.configure_branch(result)
1205
self.copy_content_into(
1206
result, revision_id=revision_id, tag_selector=tag_selector)
1244
self.copy_content_into(result, revision_id=revision_id)
1209
def sprout(self, to_controldir, revision_id=None, repository_policy=None,
1210
repository=None, lossy=False, tag_selector=None):
1211
"""Create a new line of development from the branch, into to_controldir.
1250
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1252
"""Create a new line of development from the branch, into to_bzrdir.
1213
to_controldir controls the branch format.
1254
to_bzrdir controls the branch format.
1215
1256
revision_id: if not None, the revision history in the new branch will
1216
1257
be truncated to end with revision_id.
1218
if (repository_policy is not None
1219
and repository_policy.requires_stacking()):
1220
to_controldir._format.require_stacking(_skip_repo=True)
1221
result = to_controldir.create_branch(repository=repository)
1223
raise errors.LossyPushToSameVCS(self, result)
1224
with self.lock_read(), result.lock_write():
1259
if (repository_policy is not None and
1260
repository_policy.requires_stacking()):
1261
to_bzrdir._format.require_stacking(_skip_repo=True)
1262
result = to_bzrdir.create_branch(repository=repository)
1225
1265
if repository_policy is not None:
1226
1266
repository_policy.configure_branch(result)
1227
self.copy_content_into(
1228
result, revision_id=revision_id, tag_selector=tag_selector)
1267
self.copy_content_into(result, revision_id=revision_id)
1229
1268
master_url = self.get_bound_location()
1230
1269
if master_url is None:
1231
result.set_parent(self.user_url)
1270
result.set_parent(self.bzrdir.root_transport.base)
1233
1272
result.set_parent(master_url)
1236
1277
def _synchronize_history(self, destination, revision_id):
1252
1293
graph = self.repository.get_graph()
1254
revno = graph.find_distance_to_null(
1255
revision_id, [(source_revision_id, source_revno)])
1295
revno = graph.find_distance_to_null(revision_id,
1296
[(source_revision_id, source_revno)])
1256
1297
except errors.GhostRevisionsHaveNoRevno:
1257
1298
# Default to 1, if we can't find anything else
1259
1300
destination.set_last_revision_info(revno, revision_id)
1261
def copy_content_into(self, destination, revision_id=None, tag_selector=None):
1302
def copy_content_into(self, destination, revision_id=None):
1262
1303
"""Copy the content of self into destination.
1264
1305
revision_id: if not None, the revision history in the new branch will
1265
1306
be truncated to end with revision_id.
1266
tag_selector: Optional callback that receives a tag name
1267
and should return a boolean to indicate whether a tag should be copied
1269
1308
return InterBranch.get(self, destination).copy_content_into(
1270
revision_id=revision_id, tag_selector=tag_selector)
1309
revision_id=revision_id)
1272
1311
def update_references(self, target):
1273
if not self._format.supports_reference_locations:
1275
return InterBranch.get(self, target).update_references()
1312
if not getattr(self._format, 'supports_reference_locations', False):
1314
reference_dict = self._get_all_reference_info()
1315
if len(reference_dict) == 0:
1317
old_base = self.base
1318
new_base = target.base
1319
target_reference_dict = target._get_all_reference_info()
1320
for file_id, (tree_path, branch_location) in viewitems(reference_dict):
1321
branch_location = urlutils.rebase_url(branch_location,
1323
target_reference_dict.setdefault(
1324
file_id, (tree_path, branch_location))
1325
target._set_all_reference_info(target_reference_dict)
1277
1328
def check(self, refs):
1278
1329
"""Check consistency of the branch.
1287
1338
branch._get_check_refs()
1288
1339
:return: A BranchCheckResult.
1290
with self.lock_read():
1291
result = BranchCheckResult(self)
1292
last_revno, last_revision_id = self.last_revision_info()
1293
actual_revno = refs[('lefthand-distance', last_revision_id)]
1294
if actual_revno != last_revno:
1295
result.errors.append(errors.BzrCheckError(
1296
'revno does not match len(mainline) %s != %s' % (
1297
last_revno, actual_revno)))
1298
# TODO: We should probably also check that self.revision_history
1299
# matches the repository for older branch formats.
1300
# If looking for the code that cross-checks repository parents
1301
# against the Graph.iter_lefthand_ancestry output, that is now a
1302
# repository specific check.
1341
result = BranchCheckResult(self)
1342
last_revno, last_revision_id = self.last_revision_info()
1343
actual_revno = refs[('lefthand-distance', last_revision_id)]
1344
if actual_revno != last_revno:
1345
result.errors.append(errors.BzrCheckError(
1346
'revno does not match len(mainline) %s != %s' % (
1347
last_revno, actual_revno)))
1348
# TODO: We should probably also check that self.revision_history
1349
# matches the repository for older branch formats.
1350
# If looking for the code that cross-checks repository parents against
1351
# the Graph.iter_lefthand_ancestry output, that is now a repository
1305
1355
def _get_checkout_format(self, lightweight=False):
1306
1356
"""Return the most suitable metadir for a checkout of this branch.
1307
1357
Weaves are used if this branch's repository uses weaves.
1309
format = self.repository.controldir.checkout_metadir()
1359
format = self.repository.bzrdir.checkout_metadir()
1310
1360
format.set_branch_format(self._format)
1313
1363
def create_clone_on_transport(self, to_transport, revision_id=None,
1314
stacked_on=None, create_prefix=False,
1315
use_existing_dir=False, no_tree=None,
1364
stacked_on=None, create_prefix=False, use_existing_dir=False,
1317
1366
"""Create a clone of this branch and its bzrdir.
1319
1368
:param to_transport: The transport to clone onto.
1391
1438
accelerator_tree=accelerator_tree,
1392
1439
hardlink=hardlink)
1393
1440
basis_tree = tree.basis_tree()
1394
with basis_tree.lock_read():
1395
for path in basis_tree.iter_references():
1396
reference_parent = tree.reference_parent(path)
1397
if reference_parent is None:
1398
warning('Branch location for %s unknown.', path)
1400
reference_parent.create_checkout(
1402
basis_tree.get_reference_revision(path), lightweight)
1441
basis_tree.lock_read()
1443
for path, file_id in basis_tree.iter_references():
1444
reference_parent = self.reference_parent(file_id, path)
1445
reference_parent.create_checkout(tree.abspath(path),
1446
basis_tree.get_reference_revision(file_id, path),
1405
1453
def reconcile(self, thorough=True):
1406
"""Make sure the data stored in this branch is consistent.
1408
:return: A `ReconcileResult` object.
1454
"""Make sure the data stored in this branch is consistent."""
1455
from breezy.reconcile import BranchReconciler
1456
reconciler = BranchReconciler(self, thorough=thorough)
1457
reconciler.reconcile()
1460
def reference_parent(self, file_id, path, possible_transports=None):
1461
"""Return the parent branch for a tree-reference file_id
1463
:param file_id: The file_id of the tree reference
1464
:param path: The path of the file_id in the tree
1465
:return: A branch associated with the file_id
1410
raise NotImplementedError(self.reconcile)
1467
# FIXME should provide multiple branches, based on config
1468
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1469
possible_transports=possible_transports)
1412
1471
def supports_tags(self):
1413
1472
return self._format.supports_tags()
1680
1720
"basis revision. hooks MUST NOT modify this delta. "
1681
1721
" future_tree is an in-memory tree obtained from "
1682
1722
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1724
self.add_hook('post_commit',
1686
1725
"Called in the bzr client after a commit has completed. "
1687
1726
"post_commit is called with (local, master, old_revno, old_revid, "
1688
1727
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1689
1728
"commit to a branch.", (0, 15))
1729
self.add_hook('post_uncommit',
1692
1730
"Called in the bzr client after an uncommit completes. "
1693
1731
"post_uncommit is called with (local, master, old_revno, "
1694
1732
"old_revid, new_revno, new_revid) where local is the local branch "
1695
1733
"or None, master is the target branch, and an empty branch "
1696
1734
"receives new_revno of 0, new_revid of None.", (0, 15))
1698
'pre_change_branch_tip',
1735
self.add_hook('pre_change_branch_tip',
1699
1736
"Called in bzr client and server before a change to the tip of a "
1700
1737
"branch is made. pre_change_branch_tip is called with a "
1701
1738
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1702
1739
"commit, uncommit will all trigger this hook.", (1, 6))
1704
'post_change_branch_tip',
1740
self.add_hook('post_change_branch_tip',
1705
1741
"Called in bzr client and server after a change to the tip of a "
1706
1742
"branch is made. post_change_branch_tip is called with a "
1707
1743
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1708
1744
"commit, uncommit will all trigger this hook.", (1, 4))
1710
'transform_fallback_location',
1745
self.add_hook('transform_fallback_location',
1711
1746
"Called when a stacked branch is activating its fallback "
1712
1747
"locations. transform_fallback_location is called with (branch, "
1713
1748
"url), and should return a new url. Returning the same url "
1895
1928
# formats which have no format string are not discoverable
1896
1929
# and not independently creatable, so are not registered.
1897
1930
format_registry.register_lazy(
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1931
"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1899
1932
"BzrBranchFormat5")
1900
1933
format_registry.register_lazy(
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1934
"Bazaar Branch Format 6 (bzr 0.15)\n",
1902
1935
"breezy.bzr.branch", "BzrBranchFormat6")
1903
1936
format_registry.register_lazy(
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1937
"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1905
1938
"breezy.bzr.branch", "BzrBranchFormat7")
1906
1939
format_registry.register_lazy(
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1940
"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1908
1941
"breezy.bzr.branch", "BzrBranchFormat8")
1909
1942
format_registry.register_lazy(
1910
b"Bazaar-NG Branch Reference Format 1\n",
1943
"Bazaar-NG Branch Reference Format 1\n",
1911
1944
"breezy.bzr.branch", "BranchReferenceFormat")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1946
format_registry.set_default_key("Bazaar Branch Format 7 (needs bzr 1.6)\n")
1916
1949
class BranchWriteLockResult(LogicalLockResult):
1917
1950
"""The result of write locking a branch.
1919
:ivar token: The token obtained from the underlying branch lock, or
1952
:ivar branch_token: The token obtained from the underlying branch lock, or
1921
1954
:ivar unlock: A callable which will unlock the lock.
1957
def __init__(self, unlock, branch_token):
1958
LogicalLockResult.__init__(self, unlock)
1959
self.branch_token = branch_token
1924
1961
def __repr__(self):
1925
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
1962
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
1928
1966
######################################################################
2070
2103
raise NotImplementedError(self.pull)
2072
2106
def push(self, overwrite=False, stop_revision=None, lossy=False,
2073
_override_hook_source_branch=None, tag_selector=None):
2107
_override_hook_source_branch=None):
2074
2108
"""Mirror the source branch into the target branch.
2076
2110
The source branch is considered to be 'local', having low latency.
2078
2112
raise NotImplementedError(self.push)
2080
def copy_content_into(self, revision_id=None, tag_selector=None):
2115
def copy_content_into(self, revision_id=None):
2081
2116
"""Copy the content of source into target
2084
if not None, the revision history in the new branch will
2085
be truncated to end with revision_id.
2086
:param tag_selector: Optional callback that can decide
2087
to copy or not copy tags.
2118
revision_id: if not None, the revision history in the new branch will
2119
be truncated to end with revision_id.
2089
2121
raise NotImplementedError(self.copy_content_into)
2091
def fetch(self, stop_revision=None, limit=None, lossy=False):
2124
def fetch(self, stop_revision=None, limit=None):
2092
2125
"""Fetch revisions.
2094
2127
:param stop_revision: Last revision to fetch
2095
2128
:param limit: Optional rough limit of revisions to fetch
2096
:return: FetchResult object
2098
2130
raise NotImplementedError(self.fetch)
2100
def update_references(self):
2101
"""Import reference information from source to target.
2103
raise NotImplementedError(self.update_references)
2106
2133
def _fix_overwrite_type(overwrite):
2107
2134
if isinstance(overwrite, bool):
2131
2158
return format._custom_format
2134
def copy_content_into(self, revision_id=None, tag_selector=None):
2162
def copy_content_into(self, revision_id=None):
2135
2163
"""Copy the content of source into target
2137
2165
revision_id: if not None, the revision history in the new branch will
2138
2166
be truncated to end with revision_id.
2140
with self.source.lock_read(), self.target.lock_write():
2141
self.source._synchronize_history(self.target, revision_id)
2142
self.update_references()
2144
parent = self.source.get_parent()
2145
except errors.InaccessibleParent as e:
2146
mutter('parent was not accessible to copy: %s', str(e))
2149
self.target.set_parent(parent)
2150
if self.source._push_should_merge_tags():
2151
self.source.tags.merge_to(self.target.tags, selector=tag_selector)
2168
self.source.update_references(self.target)
2169
self.source._synchronize_history(self.target, revision_id)
2171
parent = self.source.get_parent()
2172
except errors.InaccessibleParent as e:
2173
mutter('parent was not accessible to copy: %s', e)
2176
self.target.set_parent(parent)
2177
if self.source._push_should_merge_tags():
2178
self.source.tags.merge_to(self.target.tags)
2153
def fetch(self, stop_revision=None, limit=None, lossy=False):
2181
def fetch(self, stop_revision=None, limit=None):
2154
2182
if self.target.base == self.source.base:
2156
with self.source.lock_read(), self.target.lock_write():
2184
self.source.lock_read()
2157
2186
fetch_spec_factory = fetch.FetchSpecFactory()
2158
2187
fetch_spec_factory.source_branch = self.source
2159
2188
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2160
2189
fetch_spec_factory.source_repo = self.source.repository
2161
2190
fetch_spec_factory.target_repo = self.target.repository
2162
fetch_spec_factory.target_repo_kind = (
2163
fetch.TargetRepoKinds.PREEXISTING)
2191
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
2164
2192
fetch_spec_factory.limit = limit
2165
2193
fetch_spec = fetch_spec_factory.make_fetch_spec()
2166
return self.target.repository.fetch(
2167
self.source.repository,
2194
return self.target.repository.fetch(self.source.repository,
2169
2195
fetch_spec=fetch_spec)
2197
self.source.unlock()
2171
2200
def _update_revisions(self, stop_revision=None, overwrite=False,
2173
with self.source.lock_read(), self.target.lock_write():
2174
other_revno, other_last_revision = self.source.last_revision_info()
2175
stop_revno = None # unknown
2176
if stop_revision is None:
2177
stop_revision = other_last_revision
2178
if _mod_revision.is_null(stop_revision):
2179
# if there are no commits, we're done.
2181
stop_revno = other_revno
2202
other_revno, other_last_revision = self.source.last_revision_info()
2203
stop_revno = None # unknown
2204
if stop_revision is None:
2205
stop_revision = other_last_revision
2206
if _mod_revision.is_null(stop_revision):
2207
# if there are no commits, we're done.
2209
stop_revno = other_revno
2183
# what's the current last revision, before we fetch [and change it
2185
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2186
# we fetch here so that we don't process data twice in the common
2187
# case of having something to pull, and so that the check for
2188
# already merged can operate on the just fetched graph, which will
2189
# be cached in memory.
2190
self.fetch(stop_revision=stop_revision)
2191
# Check to see if one is an ancestor of the other
2194
graph = self.target.repository.get_graph()
2195
if self.target._check_if_descendant_or_diverged(
2196
stop_revision, last_rev, graph, self.source):
2197
# stop_revision is a descendant of last_rev, but we aren't
2198
# overwriting, so we're done.
2200
if stop_revno is None:
2202
graph = self.target.repository.get_graph()
2203
this_revno, this_last_revision = \
2211
# what's the current last revision, before we fetch [and change it
2213
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2214
# we fetch here so that we don't process data twice in the common
2215
# case of having something to pull, and so that the check for
2216
# already merged can operate on the just fetched graph, which will
2217
# be cached in memory.
2218
self.fetch(stop_revision=stop_revision)
2219
# Check to see if one is an ancestor of the other
2222
graph = self.target.repository.get_graph()
2223
if self.target._check_if_descendant_or_diverged(
2224
stop_revision, last_rev, graph, self.source):
2225
# stop_revision is a descendant of last_rev, but we aren't
2226
# overwriting, so we're done.
2228
if stop_revno is None:
2230
graph = self.target.repository.get_graph()
2231
this_revno, this_last_revision = \
2204
2232
self.target.last_revision_info()
2205
stop_revno = graph.find_distance_to_null(
2206
stop_revision, [(other_last_revision, other_revno),
2207
(this_last_revision, this_revno)])
2208
self.target.set_last_revision_info(stop_revno, stop_revision)
2233
stop_revno = graph.find_distance_to_null(stop_revision,
2234
[(other_last_revision, other_revno),
2235
(this_last_revision, this_revno)])
2236
self.target.set_last_revision_info(stop_revno, stop_revision)
2210
2239
def pull(self, overwrite=False, stop_revision=None,
2211
2240
possible_transports=None, run_hooks=True,
2212
_override_hook_target=None, local=False,
2241
_override_hook_target=None, local=False):
2214
2242
"""Pull from source into self, updating my master if any.
2216
2244
:param run_hooks: Private parameter - if false, this branch
2217
2245
is being called because it's the master of the primary branch,
2218
2246
so it should not run its hooks.
2220
with contextlib.ExitStack() as exit_stack:
2221
exit_stack.enter_context(self.target.lock_write())
2222
bound_location = self.target.get_bound_location()
2223
if local and not bound_location:
2224
raise errors.LocalRequiresBoundBranch()
2225
master_branch = None
2226
source_is_master = False
2228
# bound_location comes from a config file, some care has to be
2229
# taken to relate it to source.user_url
2230
normalized = urlutils.normalize_url(bound_location)
2232
relpath = self.source.user_transport.relpath(normalized)
2233
source_is_master = (relpath == '')
2234
except (errors.PathNotChild, urlutils.InvalidURL):
2235
source_is_master = False
2236
if not local and bound_location and not source_is_master:
2237
# not pulling from master, so we need to update master.
2238
master_branch = self.target.get_master_branch(
2239
possible_transports)
2240
exit_stack.enter_context(master_branch.lock_write())
2248
bound_location = self.target.get_bound_location()
2249
if local and not bound_location:
2250
raise errors.LocalRequiresBoundBranch()
2251
master_branch = None
2252
source_is_master = False
2254
# bound_location comes from a config file, some care has to be
2255
# taken to relate it to source.user_url
2256
normalized = urlutils.normalize_url(bound_location)
2258
relpath = self.source.user_transport.relpath(normalized)
2259
source_is_master = (relpath == '')
2260
except (errors.PathNotChild, errors.InvalidURL):
2261
source_is_master = False
2262
if not local and bound_location and not source_is_master:
2263
# not pulling from master, so we need to update master.
2264
master_branch = self.target.get_master_branch(possible_transports)
2265
master_branch.lock_write()
2241
2267
if master_branch:
2242
2268
# pull from source into master.
2244
self.source, overwrite, stop_revision, run_hooks=False,
2245
tag_selector=tag_selector)
2247
overwrite, stop_revision, _hook_master=master_branch,
2269
master_branch.pull(self.source, overwrite, stop_revision,
2271
return self._pull(overwrite,
2272
stop_revision, _hook_master=master_branch,
2248
2273
run_hooks=run_hooks,
2249
2274
_override_hook_target=_override_hook_target,
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
2275
merge_tags_to_master=not source_is_master)
2278
master_branch.unlock()
2253
2280
def push(self, overwrite=False, stop_revision=None, lossy=False,
2254
_override_hook_source_branch=None, tag_selector=None):
2281
_override_hook_source_branch=None):
2255
2282
"""See InterBranch.push.
2257
2284
This is the basic concrete implementation of push()
2266
2293
# TODO: Public option to disable running hooks - should be trivial but
2270
if _override_hook_source_branch:
2271
result.source_branch = _override_hook_source_branch
2272
for hook in Branch.hooks['post_push']:
2275
with self.source.lock_read(), self.target.lock_write():
2276
bound_location = self.target.get_bound_location()
2277
if bound_location and self.target.base != bound_location:
2278
# there is a master branch.
2280
# XXX: Why the second check? Is it even supported for a branch
2281
# to be bound to itself? -- mbp 20070507
2282
master_branch = self.target.get_master_branch()
2283
with master_branch.lock_write():
2284
# push into the master from the source branch.
2285
master_inter = InterBranch.get(self.source, master_branch)
2286
master_inter._basic_push(
2287
overwrite, stop_revision, tag_selector=tag_selector)
2288
# and push into the target branch from the source. Note
2289
# that we push from the source branch again, because it's
2290
# considered the highest bandwidth repository.
2291
result = self._basic_push(
2292
overwrite, stop_revision, tag_selector=tag_selector)
2293
result.master_branch = master_branch
2294
result.local_branch = self.target
2297
master_branch = None
2299
result = self._basic_push(
2300
overwrite, stop_revision, tag_selector=tag_selector)
2301
# TODO: Why set master_branch and local_branch if there's no
2302
# binding? Maybe cleaner to just leave them unset? -- mbp
2304
result.master_branch = self.target
2305
result.local_branch = None
2309
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
2296
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
2297
op.add_cleanup(self.source.lock_read().unlock)
2298
op.add_cleanup(self.target.lock_write().unlock)
2299
return op.run(overwrite, stop_revision,
2300
_override_hook_source_branch=_override_hook_source_branch)
2302
def _basic_push(self, overwrite, stop_revision):
2310
2303
"""Basic implementation of push without bound branches or hooks.
2312
2305
Must be called with source read locked and target write locked.
2315
2308
result.source_branch = self.source
2316
2309
result.target_branch = self.target
2317
2310
result.old_revno, result.old_revid = self.target.last_revision_info()
2311
self.source.update_references(self.target)
2318
2312
overwrite = _fix_overwrite_type(overwrite)
2319
2313
if result.old_revid != stop_revision:
2320
2314
# We assume that during 'push' this repository is closer than
2322
2316
graph = self.source.repository.get_graph(self.target.repository)
2323
self._update_revisions(
2324
stop_revision, overwrite=("history" in overwrite), graph=graph)
2317
self._update_revisions(stop_revision,
2318
overwrite=("history" in overwrite),
2325
2320
if self.source._push_should_merge_tags():
2326
2321
result.tag_updates, result.tag_conflicts = (
2327
2322
self.source.tags.merge_to(
2328
self.target.tags, "tags" in overwrite, selector=tag_selector))
2329
self.update_references()
2323
self.target.tags, "tags" in overwrite))
2330
2324
result.new_revno, result.new_revid = self.target.last_revision_info()
2327
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
2328
_override_hook_source_branch=None):
2329
"""Push from source into target, and into target's master if any.
2332
if _override_hook_source_branch:
2333
result.source_branch = _override_hook_source_branch
2334
for hook in Branch.hooks['post_push']:
2337
bound_location = self.target.get_bound_location()
2338
if bound_location and self.target.base != bound_location:
2339
# there is a master branch.
2341
# XXX: Why the second check? Is it even supported for a branch to
2342
# be bound to itself? -- mbp 20070507
2343
master_branch = self.target.get_master_branch()
2344
master_branch.lock_write()
2345
operation.add_cleanup(master_branch.unlock)
2346
# push into the master from the source branch.
2347
master_inter = InterBranch.get(self.source, master_branch)
2348
master_inter._basic_push(overwrite, stop_revision)
2349
# and push into the target branch from the source. Note that
2350
# we push from the source branch again, because it's considered
2351
# the highest bandwidth repository.
2352
result = self._basic_push(overwrite, stop_revision)
2353
result.master_branch = master_branch
2354
result.local_branch = self.target
2356
master_branch = None
2358
result = self._basic_push(overwrite, stop_revision)
2359
# TODO: Why set master_branch and local_branch if there's no
2360
# binding? Maybe cleaner to just leave them unset? -- mbp
2362
result.master_branch = self.target
2363
result.local_branch = None
2333
2367
def _pull(self, overwrite=False, stop_revision=None,
2334
possible_transports=None, _hook_master=None, run_hooks=True,
2335
_override_hook_target=None, local=False,
2336
merge_tags_to_master=True, tag_selector=None):
2368
possible_transports=None, _hook_master=None, run_hooks=True,
2369
_override_hook_target=None, local=False,
2370
merge_tags_to_master=True):
2337
2371
"""See Branch.pull.
2339
2373
This function is the core worker, used by GenericInterBranch.pull to
2359
2393
result.target_branch = self.target
2361
2395
result.target_branch = _override_hook_target
2362
with self.source.lock_read():
2396
self.source.lock_read()
2363
2398
# We assume that during 'pull' the target repository is closer than
2364
2399
# the source one.
2400
self.source.update_references(self.target)
2365
2401
graph = self.target.repository.get_graph(self.source.repository)
2366
# TODO: Branch formats should have a flag that indicates
2402
# TODO: Branch formats should have a flag that indicates
2367
2403
# that revno's are expensive, and pull() should honor that flag.
2368
2404
# -- JRV20090506
2369
2405
result.old_revno, result.old_revid = \
2370
2406
self.target.last_revision_info()
2371
2407
overwrite = _fix_overwrite_type(overwrite)
2372
self._update_revisions(
2373
stop_revision, overwrite=("history" in overwrite), graph=graph)
2374
# TODO: The old revid should be specified when merging tags,
2375
# so a tags implementation that versions tags can only
2408
self._update_revisions(stop_revision,
2409
overwrite=("history" in overwrite),
2411
# TODO: The old revid should be specified when merging tags,
2412
# so a tags implementation that versions tags can only
2376
2413
# pull in the most recent changes. -- JRV20090506
2377
2414
result.tag_updates, result.tag_conflicts = (
2378
self.source.tags.merge_to(
2379
self.target.tags, "tags" in overwrite,
2380
ignore_master=not merge_tags_to_master,
2381
selector=tag_selector))
2382
self.update_references()
2383
result.new_revno, result.new_revid = (
2384
self.target.last_revision_info())
2415
self.source.tags.merge_to(self.target.tags,
2416
"tags" in overwrite,
2417
ignore_master=not merge_tags_to_master))
2418
result.new_revno, result.new_revid = self.target.last_revision_info()
2385
2419
if _hook_master:
2386
2420
result.master_branch = _hook_master
2387
2421
result.local_branch = result.target_branch