485
503
* end_of_merge: When True the next node (earlier in history) is
486
504
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)
506
# Note: depth and revno values are in the context of the branch so
507
# we need the full graph to get stable numbers, regardless of the
509
if self._merge_sorted_revisions_cache is None:
510
last_revision = self.last_revision()
511
known_graph = self.repository.get_known_graph_ancestry(
513
self._merge_sorted_revisions_cache = known_graph.merge_sort(
515
filtered = self._filter_merge_sorted_revisions(
516
self._merge_sorted_revisions_cache, start_revision_id,
517
stop_revision_id, stop_rule)
518
# Make sure we don't return revisions that are not part of the
519
# start_revision_id ancestry.
520
filtered = self._filter_start_non_ancestors(filtered)
521
if direction == 'reverse':
523
if direction == 'forward':
524
return reversed(list(filtered))
526
raise ValueError('invalid direction %r' % direction)
511
528
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
512
start_revision_id, stop_revision_id,
529
start_revision_id, stop_revision_id, stop_rule):
514
530
"""Iterate over an inclusive range of sorted revisions."""
515
531
rev_iter = iter(merge_sorted_revisions)
516
532
if start_revision_id is not None:
749
772
raise NotImplementedError(self.set_last_revision_info)
751
775
def generate_revision_history(self, revision_id, last_rev=None,
752
776
other_branch=None):
753
777
"""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)
778
graph = self.repository.get_graph()
779
(last_revno, last_revid) = self.last_revision_info()
780
known_revision_ids = [
781
(last_revid, last_revno),
782
(_mod_revision.NULL_REVISION, 0),
784
if last_rev is not None:
785
if not graph.is_ancestor(last_rev, revision_id):
786
# our previous tip is not merged into stop_revision
787
raise errors.DivergedBranches(self, other_branch)
788
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
789
self.set_last_revision_info(revno, revision_id)
769
792
def set_parent(self, url):
770
793
"""See Branch.set_parent."""
771
794
# TODO: Maybe delete old location files?
795
818
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)
819
raise errors.UnstackableBranchFormat(self._format, self.user_url)
820
# XXX: Changing from one fallback repository to another does not check
821
# that all the data you need is present in the new fallback.
822
# Possibly it should.
823
self._check_stackable_repo()
826
old_url = self.get_stacked_on_url()
827
except (errors.NotStacked, errors.UnstackableBranchFormat,
828
errors.UnstackableRepositoryFormat):
832
self._activate_fallback_location(url,
833
possible_transports=[self.controldir.root_transport])
834
# write this out after the repository is stacked to avoid setting a
835
# stacked config that doesn't work.
836
self._set_config_location('stacked_on_location', url)
816
838
def _unstack(self):
817
839
"""Change a branch to be unstacked, copying data as needed.
819
841
Don't call this directly, use set_stacked_on_url(None).
821
with ui.ui_factory.nested_progress_bar() as pb:
843
pb = ui.ui_factory.nested_progress_bar()
822
845
pb.update(gettext("Unstacking"))
823
846
# The basic approach here is to fetch the tip of the branch,
824
847
# including all available ghosts, from the existing stacked
825
# repository into a new repository object without the fallbacks.
848
# repository into a new repository object without the fallbacks.
827
850
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
828
851
# correct for CHKMap repostiories
829
852
old_repository = self.repository
830
853
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))
854
raise AssertionError("can't cope with fallback repositories "
855
"of %r (fallbacks: %r)" % (old_repository,
856
old_repository._fallback_repositories))
835
857
# Open the new repository object.
836
858
# Repositories don't offer an interface to remove fallback
837
859
# repositories today; take the conceptually simpler option and just
1252
1295
graph = self.repository.get_graph()
1254
revno = graph.find_distance_to_null(
1255
revision_id, [(source_revision_id, source_revno)])
1297
revno = graph.find_distance_to_null(revision_id,
1298
[(source_revision_id, source_revno)])
1256
1299
except errors.GhostRevisionsHaveNoRevno:
1257
1300
# Default to 1, if we can't find anything else
1259
1302
destination.set_last_revision_info(revno, revision_id)
1261
def copy_content_into(self, destination, revision_id=None, tag_selector=None):
1304
def copy_content_into(self, destination, revision_id=None):
1262
1305
"""Copy the content of self into destination.
1264
1307
revision_id: if not None, the revision history in the new branch will
1265
1308
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
1310
return InterBranch.get(self, destination).copy_content_into(
1270
revision_id=revision_id, tag_selector=tag_selector)
1311
revision_id=revision_id)
1272
1313
def update_references(self, target):
1273
if not self._format.supports_reference_locations:
1275
return InterBranch.get(self, target).update_references()
1314
if not getattr(self._format, 'supports_reference_locations', False):
1316
reference_dict = self._get_all_reference_info()
1317
if len(reference_dict) == 0:
1319
old_base = self.base
1320
new_base = target.base
1321
target_reference_dict = target._get_all_reference_info()
1322
for file_id, (tree_path, branch_location) in viewitems(reference_dict):
1323
branch_location = urlutils.rebase_url(branch_location,
1325
target_reference_dict.setdefault(
1326
file_id, (tree_path, branch_location))
1327
target._set_all_reference_info(target_reference_dict)
1277
1330
def check(self, refs):
1278
1331
"""Check consistency of the branch.
1391
1440
accelerator_tree=accelerator_tree,
1392
1441
hardlink=hardlink)
1393
1442
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)
1443
basis_tree.lock_read()
1445
for path, file_id in basis_tree.iter_references():
1446
reference_parent = self.reference_parent(file_id, path)
1447
reference_parent.create_checkout(tree.abspath(path),
1448
basis_tree.get_reference_revision(file_id, path),
1405
1455
def reconcile(self, thorough=True):
1406
"""Make sure the data stored in this branch is consistent.
1408
:return: A `ReconcileResult` object.
1456
"""Make sure the data stored in this branch is consistent."""
1457
from breezy.reconcile import BranchReconciler
1458
reconciler = BranchReconciler(self, thorough=thorough)
1459
reconciler.reconcile()
1462
def reference_parent(self, file_id, path, possible_transports=None):
1463
"""Return the parent branch for a tree-reference file_id
1465
:param file_id: The file_id of the tree reference
1466
:param path: The path of the file_id in the tree
1467
:return: A branch associated with the file_id
1410
raise NotImplementedError(self.reconcile)
1469
# FIXME should provide multiple branches, based on config
1470
return Branch.open(self.controldir.root_transport.clone(path).base,
1471
possible_transports=possible_transports)
1412
1473
def supports_tags(self):
1413
1474
return self._format.supports_tags()
1680
1722
"basis revision. hooks MUST NOT modify this delta. "
1681
1723
" future_tree is an in-memory tree obtained from "
1682
1724
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1726
self.add_hook('post_commit',
1686
1727
"Called in the bzr client after a commit has completed. "
1687
1728
"post_commit is called with (local, master, old_revno, old_revid, "
1688
1729
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1689
1730
"commit to a branch.", (0, 15))
1731
self.add_hook('post_uncommit',
1692
1732
"Called in the bzr client after an uncommit completes. "
1693
1733
"post_uncommit is called with (local, master, old_revno, "
1694
1734
"old_revid, new_revno, new_revid) where local is the local branch "
1695
1735
"or None, master is the target branch, and an empty branch "
1696
1736
"receives new_revno of 0, new_revid of None.", (0, 15))
1698
'pre_change_branch_tip',
1737
self.add_hook('pre_change_branch_tip',
1699
1738
"Called in bzr client and server before a change to the tip of a "
1700
1739
"branch is made. pre_change_branch_tip is called with a "
1701
1740
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1702
1741
"commit, uncommit will all trigger this hook.", (1, 6))
1704
'post_change_branch_tip',
1742
self.add_hook('post_change_branch_tip',
1705
1743
"Called in bzr client and server after a change to the tip of a "
1706
1744
"branch is made. post_change_branch_tip is called with a "
1707
1745
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1708
1746
"commit, uncommit will all trigger this hook.", (1, 4))
1710
'transform_fallback_location',
1747
self.add_hook('transform_fallback_location',
1711
1748
"Called when a stacked branch is activating its fallback "
1712
1749
"locations. transform_fallback_location is called with (branch, "
1713
1750
"url), and should return a new url. Returning the same url "
1895
1930
# formats which have no format string are not discoverable
1896
1931
# and not independently creatable, so are not registered.
1897
1932
format_registry.register_lazy(
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1933
"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1899
1934
"BzrBranchFormat5")
1900
1935
format_registry.register_lazy(
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1936
"Bazaar Branch Format 6 (bzr 0.15)\n",
1902
1937
"breezy.bzr.branch", "BzrBranchFormat6")
1903
1938
format_registry.register_lazy(
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1939
"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1905
1940
"breezy.bzr.branch", "BzrBranchFormat7")
1906
1941
format_registry.register_lazy(
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1942
"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1908
1943
"breezy.bzr.branch", "BzrBranchFormat8")
1909
1944
format_registry.register_lazy(
1910
b"Bazaar-NG Branch Reference Format 1\n",
1945
"Bazaar-NG Branch Reference Format 1\n",
1911
1946
"breezy.bzr.branch", "BranchReferenceFormat")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1948
format_registry.set_default_key("Bazaar Branch Format 7 (needs bzr 1.6)\n")
1916
1951
class BranchWriteLockResult(LogicalLockResult):
1917
1952
"""The result of write locking a branch.
1919
:ivar token: The token obtained from the underlying branch lock, or
1954
:ivar branch_token: The token obtained from the underlying branch lock, or
1921
1956
:ivar unlock: A callable which will unlock the lock.
1959
def __init__(self, unlock, branch_token):
1960
LogicalLockResult.__init__(self, unlock)
1961
self.branch_token = branch_token
1924
1963
def __repr__(self):
1925
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
1964
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
1928
1968
######################################################################
2070
2105
raise NotImplementedError(self.pull)
2072
2108
def push(self, overwrite=False, stop_revision=None, lossy=False,
2073
_override_hook_source_branch=None, tag_selector=None):
2109
_override_hook_source_branch=None):
2074
2110
"""Mirror the source branch into the target branch.
2076
2112
The source branch is considered to be 'local', having low latency.
2078
2114
raise NotImplementedError(self.push)
2080
def copy_content_into(self, revision_id=None, tag_selector=None):
2117
def copy_content_into(self, revision_id=None):
2081
2118
"""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.
2120
revision_id: if not None, the revision history in the new branch will
2121
be truncated to end with revision_id.
2089
2123
raise NotImplementedError(self.copy_content_into)
2091
def fetch(self, stop_revision=None, limit=None, lossy=False):
2126
def fetch(self, stop_revision=None, limit=None):
2092
2127
"""Fetch revisions.
2094
2129
:param stop_revision: Last revision to fetch
2095
2130
:param limit: Optional rough limit of revisions to fetch
2096
:return: FetchResult object
2098
2132
raise NotImplementedError(self.fetch)
2100
def update_references(self):
2101
"""Import reference information from source to target.
2103
raise NotImplementedError(self.update_references)
2106
2135
def _fix_overwrite_type(overwrite):
2107
2136
if isinstance(overwrite, bool):
2131
2160
return format._custom_format
2134
def copy_content_into(self, revision_id=None, tag_selector=None):
2164
def copy_content_into(self, revision_id=None):
2135
2165
"""Copy the content of source into target
2137
2167
revision_id: if not None, the revision history in the new branch will
2138
2168
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)
2170
self.source.update_references(self.target)
2171
self.source._synchronize_history(self.target, revision_id)
2173
parent = self.source.get_parent()
2174
except errors.InaccessibleParent as e:
2175
mutter('parent was not accessible to copy: %s', e)
2178
self.target.set_parent(parent)
2179
if self.source._push_should_merge_tags():
2180
self.source.tags.merge_to(self.target.tags)
2153
def fetch(self, stop_revision=None, limit=None, lossy=False):
2183
def fetch(self, stop_revision=None, limit=None):
2154
2184
if self.target.base == self.source.base:
2156
with self.source.lock_read(), self.target.lock_write():
2186
self.source.lock_read()
2157
2188
fetch_spec_factory = fetch.FetchSpecFactory()
2158
2189
fetch_spec_factory.source_branch = self.source
2159
2190
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2160
2191
fetch_spec_factory.source_repo = self.source.repository
2161
2192
fetch_spec_factory.target_repo = self.target.repository
2162
fetch_spec_factory.target_repo_kind = (
2163
fetch.TargetRepoKinds.PREEXISTING)
2193
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
2164
2194
fetch_spec_factory.limit = limit
2165
2195
fetch_spec = fetch_spec_factory.make_fetch_spec()
2166
return self.target.repository.fetch(
2167
self.source.repository,
2196
return self.target.repository.fetch(self.source.repository,
2169
2197
fetch_spec=fetch_spec)
2199
self.source.unlock()
2171
2202
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
2204
other_revno, other_last_revision = self.source.last_revision_info()
2205
stop_revno = None # unknown
2206
if stop_revision is None:
2207
stop_revision = other_last_revision
2208
if _mod_revision.is_null(stop_revision):
2209
# if there are no commits, we're done.
2211
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 = \
2213
# what's the current last revision, before we fetch [and change it
2215
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2216
# we fetch here so that we don't process data twice in the common
2217
# case of having something to pull, and so that the check for
2218
# already merged can operate on the just fetched graph, which will
2219
# be cached in memory.
2220
self.fetch(stop_revision=stop_revision)
2221
# Check to see if one is an ancestor of the other
2224
graph = self.target.repository.get_graph()
2225
if self.target._check_if_descendant_or_diverged(
2226
stop_revision, last_rev, graph, self.source):
2227
# stop_revision is a descendant of last_rev, but we aren't
2228
# overwriting, so we're done.
2230
if stop_revno is None:
2232
graph = self.target.repository.get_graph()
2233
this_revno, this_last_revision = \
2204
2234
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)
2235
stop_revno = graph.find_distance_to_null(stop_revision,
2236
[(other_last_revision, other_revno),
2237
(this_last_revision, this_revno)])
2238
self.target.set_last_revision_info(stop_revno, stop_revision)
2210
2241
def pull(self, overwrite=False, stop_revision=None,
2211
2242
possible_transports=None, run_hooks=True,
2212
_override_hook_target=None, local=False,
2243
_override_hook_target=None, local=False):
2214
2244
"""Pull from source into self, updating my master if any.
2216
2246
:param run_hooks: Private parameter - if false, this branch
2217
2247
is being called because it's the master of the primary branch,
2218
2248
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())
2250
bound_location = self.target.get_bound_location()
2251
if local and not bound_location:
2252
raise errors.LocalRequiresBoundBranch()
2253
master_branch = None
2254
source_is_master = False
2256
# bound_location comes from a config file, some care has to be
2257
# taken to relate it to source.user_url
2258
normalized = urlutils.normalize_url(bound_location)
2260
relpath = self.source.user_transport.relpath(normalized)
2261
source_is_master = (relpath == '')
2262
except (errors.PathNotChild, urlutils.InvalidURL):
2263
source_is_master = False
2264
if not local and bound_location and not source_is_master:
2265
# not pulling from master, so we need to update master.
2266
master_branch = self.target.get_master_branch(possible_transports)
2267
master_branch.lock_write()
2241
2269
if master_branch:
2242
2270
# 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,
2271
master_branch.pull(self.source, overwrite, stop_revision,
2273
return self._pull(overwrite,
2274
stop_revision, _hook_master=master_branch,
2248
2275
run_hooks=run_hooks,
2249
2276
_override_hook_target=_override_hook_target,
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
2277
merge_tags_to_master=not source_is_master)
2280
master_branch.unlock()
2253
2282
def push(self, overwrite=False, stop_revision=None, lossy=False,
2254
_override_hook_source_branch=None, tag_selector=None):
2283
_override_hook_source_branch=None):
2255
2284
"""See InterBranch.push.
2257
2286
This is the basic concrete implementation of push()
2266
2295
# 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):
2298
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
2299
op.add_cleanup(self.source.lock_read().unlock)
2300
op.add_cleanup(self.target.lock_write().unlock)
2301
return op.run(overwrite, stop_revision,
2302
_override_hook_source_branch=_override_hook_source_branch)
2304
def _basic_push(self, overwrite, stop_revision):
2310
2305
"""Basic implementation of push without bound branches or hooks.
2312
2307
Must be called with source read locked and target write locked.
2315
2310
result.source_branch = self.source
2316
2311
result.target_branch = self.target
2317
2312
result.old_revno, result.old_revid = self.target.last_revision_info()
2313
self.source.update_references(self.target)
2318
2314
overwrite = _fix_overwrite_type(overwrite)
2319
2315
if result.old_revid != stop_revision:
2320
2316
# We assume that during 'push' this repository is closer than
2322
2318
graph = self.source.repository.get_graph(self.target.repository)
2323
self._update_revisions(
2324
stop_revision, overwrite=("history" in overwrite), graph=graph)
2319
self._update_revisions(stop_revision,
2320
overwrite=("history" in overwrite),
2325
2322
if self.source._push_should_merge_tags():
2326
2323
result.tag_updates, result.tag_conflicts = (
2327
2324
self.source.tags.merge_to(
2328
self.target.tags, "tags" in overwrite, selector=tag_selector))
2329
self.update_references()
2325
self.target.tags, "tags" in overwrite))
2330
2326
result.new_revno, result.new_revid = self.target.last_revision_info()
2329
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
2330
_override_hook_source_branch=None):
2331
"""Push from source into target, and into target's master if any.
2334
if _override_hook_source_branch:
2335
result.source_branch = _override_hook_source_branch
2336
for hook in Branch.hooks['post_push']:
2339
bound_location = self.target.get_bound_location()
2340
if bound_location and self.target.base != bound_location:
2341
# there is a master branch.
2343
# XXX: Why the second check? Is it even supported for a branch to
2344
# be bound to itself? -- mbp 20070507
2345
master_branch = self.target.get_master_branch()
2346
master_branch.lock_write()
2347
operation.add_cleanup(master_branch.unlock)
2348
# push into the master from the source branch.
2349
master_inter = InterBranch.get(self.source, master_branch)
2350
master_inter._basic_push(overwrite, stop_revision)
2351
# and push into the target branch from the source. Note that
2352
# we push from the source branch again, because it's considered
2353
# the highest bandwidth repository.
2354
result = self._basic_push(overwrite, stop_revision)
2355
result.master_branch = master_branch
2356
result.local_branch = self.target
2358
master_branch = None
2360
result = self._basic_push(overwrite, stop_revision)
2361
# TODO: Why set master_branch and local_branch if there's no
2362
# binding? Maybe cleaner to just leave them unset? -- mbp
2364
result.master_branch = self.target
2365
result.local_branch = None
2333
2369
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):
2370
possible_transports=None, _hook_master=None, run_hooks=True,
2371
_override_hook_target=None, local=False,
2372
merge_tags_to_master=True):
2337
2373
"""See Branch.pull.
2339
2375
This function is the core worker, used by GenericInterBranch.pull to
2359
2395
result.target_branch = self.target
2361
2397
result.target_branch = _override_hook_target
2362
with self.source.lock_read():
2398
self.source.lock_read()
2363
2400
# We assume that during 'pull' the target repository is closer than
2364
2401
# the source one.
2402
self.source.update_references(self.target)
2365
2403
graph = self.target.repository.get_graph(self.source.repository)
2366
# TODO: Branch formats should have a flag that indicates
2404
# TODO: Branch formats should have a flag that indicates
2367
2405
# that revno's are expensive, and pull() should honor that flag.
2368
2406
# -- JRV20090506
2369
2407
result.old_revno, result.old_revid = \
2370
2408
self.target.last_revision_info()
2371
2409
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
2410
self._update_revisions(stop_revision,
2411
overwrite=("history" in overwrite),
2413
# TODO: The old revid should be specified when merging tags,
2414
# so a tags implementation that versions tags can only
2376
2415
# pull in the most recent changes. -- JRV20090506
2377
2416
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())
2417
self.source.tags.merge_to(self.target.tags,
2418
"tags" in overwrite,
2419
ignore_master=not merge_tags_to_master))
2420
result.new_revno, result.new_revid = self.target.last_revision_info()
2385
2421
if _hook_master:
2386
2422
result.master_branch = _hook_master
2387
2423
result.local_branch = result.target_branch