47
from .decorators import (
50
45
from .hooks import Hooks
51
46
from .inter import InterObject
52
47
from .lock import LogicalLockResult
57
from .trace import mutter, mutter_callsite, note, is_quiet
48
from .trace import mutter, mutter_callsite, note, is_quiet, warning
60
51
class UnstackableBranchFormat(errors.BzrError):
62
53
_fmt = ("The branch '%(url)s'(%(format)s) is not a stackable format. "
63
"You will need to upgrade the branch to permit branch stacking.")
54
"You will need to upgrade the branch to permit branch stacking.")
65
56
def __init__(self, format, url):
66
57
errors.BzrError.__init__(self)
177
168
For instance, if the branch is at URL/.bzr/branch,
178
169
Branch.open(URL) -> a Branch instance.
180
control = controldir.ControlDir.open(base,
181
possible_transports=possible_transports, _unsupported=_unsupported)
182
return control.open_branch(unsupported=_unsupported,
171
control = controldir.ControlDir.open(
172
base, possible_transports=possible_transports,
173
_unsupported=_unsupported)
174
return control.open_branch(
175
unsupported=_unsupported,
183
176
possible_transports=possible_transports)
186
179
def open_from_transport(transport, name=None, _unsupported=False,
187
possible_transports=None):
180
possible_transports=None):
188
181
"""Open the branch rooted at transport"""
189
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
190
return control.open_branch(name=name, unsupported=_unsupported,
182
control = controldir.ControlDir.open_from_transport(
183
transport, _unsupported)
184
return control.open_branch(
185
name=name, unsupported=_unsupported,
191
186
possible_transports=possible_transports)
199
194
Basically we keep looking up until we find the control directory or
200
195
run into the root. If there isn't one, raises NotBranchError.
201
If there is one and it is either an unrecognised format or an unsupported
202
format, UnknownFormatError or UnsupportedFormatError are raised.
203
If there is one, it is returned, along with the unused portion of url.
196
If there is one and it is either an unrecognised format or an
197
unsupported format, UnknownFormatError or UnsupportedFormatError are
198
raised. If there is one, it is returned, along with the unused portion
205
control, relpath = controldir.ControlDir.open_containing(url,
201
control, relpath = controldir.ControlDir.open_containing(
202
url, possible_transports)
207
203
branch = control.open_branch(possible_transports=possible_transports)
208
204
return (branch, relpath)
258
254
a_branch = Branch.open(url, possible_transports=possible_transports)
259
255
return a_branch.repository
261
def _get_tags_bytes(self):
262
"""Get the bytes of a serialised tags dict.
264
Note that not all branches support tags, nor do all use the same tags
265
logic: this method is specific to BasicTags. Other tag implementations
266
may use the same method name and behave differently, safely, because
267
of the double-dispatch via
268
format.make_tags->tags_instance->get_tags_dict.
270
:return: The bytes of the tags file.
271
:seealso: Branch._set_tags_bytes.
273
with self.lock_read():
274
if self._tags_bytes is None:
275
self._tags_bytes = self._transport.get_bytes('tags')
276
return self._tags_bytes
278
257
def _get_nick(self, local=False, possible_transports=None):
279
258
config = self.get_config()
280
259
# explicit overrides master, but don't look for master if local is True
380
359
provide a more efficient implementation.
382
361
if len(revno) == 1:
383
return self.get_rev_id(revno[0])
363
return self.get_rev_id(revno[0])
364
except errors.RevisionNotPresent as e:
365
raise errors.GhostRevisionsHaveNoRevno(revno[0], e.revision_id)
384
366
revision_id_to_revno = self.get_revision_id_to_revno_map()
385
367
revision_ids = [revision_id for revision_id, this_revno
386
in viewitems(revision_id_to_revno)
368
in revision_id_to_revno.items()
387
369
if revno == this_revno]
388
370
if len(revision_ids) == 1:
389
371
return revision_ids[0]
428
410
:return: A dictionary mapping revision_id => dotted revno.
429
411
This dictionary should not be modified by the caller.
413
if 'evil' in debug.debug_flags:
415
3, "get_revision_id_to_revno_map scales with ancestry.")
431
416
with self.lock_read():
432
417
if self._revision_id_to_revno_cache is not None:
433
418
mapping = self._revision_id_to_revno_cache
435
420
mapping = self._gen_revno_map()
436
421
self._cache_revision_id_to_revno(mapping)
437
# TODO: jam 20070417 Since this is being cached, should we be returning
439
# I would rather not, and instead just declare that users should not
440
# modify the return value.
422
# TODO: jam 20070417 Since this is being cached, should we be
424
# I would rather not, and instead just declare that users should
425
# not modify the return value.
443
428
def _gen_revno_map(self):
451
436
:return: A dictionary mapping revision_id => dotted revno.
453
revision_id_to_revno = dict((rev_id, revno)
454
for rev_id, depth, revno, end_of_merge
455
in self.iter_merge_sorted_revisions())
438
revision_id_to_revno = {
439
rev_id: revno for rev_id, depth, revno, end_of_merge
440
in self.iter_merge_sorted_revisions()}
456
441
return revision_id_to_revno
458
443
def iter_merge_sorted_revisions(self, start_revision_id=None,
459
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
444
stop_revision_id=None,
445
stop_rule='exclude', direction='reverse'):
460
446
"""Walk the revisions for a branch in merge sorted order.
462
448
Merge sorted order is the output from a merge-aware,
584
571
if rev_id == left_parent:
585
572
# reached the left parent after the stop_revision
587
if (not reached_stop_revision_id or
588
rev_id in revision_id_whitelist):
574
if (not reached_stop_revision_id
575
or rev_id in revision_id_whitelist):
589
576
yield (rev_id, node.merge_depth, node.revno,
591
578
if reached_stop_revision_id or rev_id == stop_revision_id:
592
579
# only do the merged revs of rev_id from now on
593
580
rev = self.repository.get_revision(rev_id)
678
670
raise errors.UpgradeRequired(self.user_url)
679
671
self.get_config_stack().set('append_revisions_only', enabled)
681
def set_reference_info(self, file_id, tree_path, branch_location):
682
"""Set the branch location to use for a tree reference."""
683
raise errors.UnsupportedOperation(self.set_reference_info, self)
685
def get_reference_info(self, file_id):
686
"""Get the tree_path and branch_location for a tree reference."""
687
raise errors.UnsupportedOperation(self.get_reference_info, self)
689
def fetch(self, from_branch, last_revision=None, limit=None):
673
def fetch(self, from_branch, stop_revision=None, limit=None, lossy=False):
690
674
"""Copy revisions from from_branch into this branch.
692
676
:param from_branch: Where to copy from.
693
:param last_revision: What revision to stop at (None for at the end
677
:param stop_revision: What revision to stop at (None for at the end
695
679
:param limit: Optional rough limit of revisions to fetch
698
682
with self.lock_write():
699
683
return InterBranch.get(from_branch, self).fetch(
700
last_revision, limit=limit)
684
stop_revision, limit=limit, lossy=lossy)
702
686
def get_bound_location(self):
703
687
"""Return the URL of the branch we are bound to.
725
709
:param revprops: Optional dictionary of revision properties.
726
710
:param revision_id: Optional revision id.
727
711
:param lossy: Whether to discard data that can not be natively
728
represented, when pushing to a foreign VCS
712
represented, when pushing to a foreign VCS
731
715
if config_stack is None:
732
716
config_stack = self.get_config_stack()
734
return self.repository.get_commit_builder(self, parents, config_stack,
735
timestamp, timezone, committer, revprops, revision_id,
718
return self.repository.get_commit_builder(
719
self, parents, config_stack, timestamp, timezone, committer,
720
revprops, revision_id, lossy)
738
722
def get_master_branch(self, possible_transports=None):
739
723
"""Return the branch we are bound to.
778
762
if not graph.is_ancestor(last_rev, revision_id):
779
763
# our previous tip is not merged into stop_revision
780
764
raise errors.DivergedBranches(self, other_branch)
781
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
765
revno = graph.find_distance_to_null(
766
revision_id, known_revision_ids)
782
767
self.set_last_revision_info(revno, revision_id)
784
769
def set_parent(self, url):
788
773
# FIXUP this and get_parent in a future branch format bump:
789
774
# read and rewrite the file. RBC 20060125
790
775
if url is not None:
791
if isinstance(url, unicode):
776
if isinstance(url, str):
793
url = url.encode('ascii')
794
779
except UnicodeEncodeError:
795
raise urlutils.InvalidURL(url,
796
"Urls must be 7-bit ascii, "
780
raise urlutils.InvalidURL(
781
url, "Urls must be 7-bit ascii, "
797
782
"use breezy.urlutils.escape")
798
783
url = urlutils.relative_url(self.base, url)
799
784
with self.lock_write():
810
795
if not self._format.supports_stacking():
811
796
raise UnstackableBranchFormat(self._format, self.user_url)
812
797
with self.lock_write():
813
# XXX: Changing from one fallback repository to another does not check
814
# that all the data you need is present in the new fallback.
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.
815
800
# Possibly it should.
816
801
self._check_stackable_repo()
819
old_url = self.get_stacked_on_url()
804
self.get_stacked_on_url()
820
805
except (errors.NotStacked, UnstackableBranchFormat,
821
errors.UnstackableRepositoryFormat):
806
errors.UnstackableRepositoryFormat):
825
self._activate_fallback_location(url,
826
possible_transports=[self.controldir.root_transport])
810
self._activate_fallback_location(
811
url, possible_transports=[self.controldir.root_transport])
827
812
# write this out after the repository is stacked to avoid setting a
828
813
# stacked config that doesn't work.
829
814
self._set_config_location('stacked_on_location', url)
834
819
Don't call this directly, use set_stacked_on_url(None).
836
pb = ui.ui_factory.nested_progress_bar()
821
with ui.ui_factory.nested_progress_bar() as pb:
838
822
pb.update(gettext("Unstacking"))
839
823
# The basic approach here is to fetch the tip of the branch,
840
824
# including all available ghosts, from the existing stacked
841
# repository into a new repository object without the fallbacks.
825
# repository into a new repository object without the fallbacks.
843
827
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
844
828
# correct for CHKMap repostiories
845
829
old_repository = self.repository
846
830
if len(old_repository._fallback_repositories) != 1:
847
raise AssertionError("can't cope with fallback repositories "
848
"of %r (fallbacks: %r)" % (old_repository,
849
old_repository._fallback_repositories))
831
raise AssertionError(
832
"can't cope with fallback repositories "
833
"of %r (fallbacks: %r)" % (
834
old_repository, old_repository._fallback_repositories))
850
835
# Open the new repository object.
851
836
# Repositories don't offer an interface to remove fallback
852
837
# repositories today; take the conceptually simpler option and just
904
889
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
905
890
except errors.TagsNotSupported:
906
891
tags_to_fetch = set()
907
fetch_spec = vf_search.NotInOtherForRevs(self.repository,
908
old_repository, required_ids=[self.last_revision()],
892
fetch_spec = vf_search.NotInOtherForRevs(
893
self.repository, old_repository,
894
required_ids=[self.last_revision()],
909
895
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
910
896
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
914
def _set_tags_bytes(self, bytes):
915
"""Mirror method for _get_tags_bytes.
917
:seealso: Branch._get_tags_bytes.
919
with self.lock_write():
920
self._tags_bytes = bytes
921
return self._transport.put_bytes('tags', bytes)
923
898
def _cache_revision_history(self, rev_history):
924
899
"""Set the cached revision history to rev_history.
1066
1041
:returns: PullResult instance
1068
return InterBranch.get(source, self).pull(overwrite=overwrite,
1069
stop_revision=stop_revision,
1043
return InterBranch.get(source, self).pull(
1044
overwrite=overwrite, stop_revision=stop_revision,
1070
1045
possible_transports=possible_transports, *args, **kwargs)
1072
1047
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1074
1049
"""Mirror this branch into target.
1076
1051
This branch is considered to be 'local', having low latency.
1078
return InterBranch.get(self, target).push(overwrite, stop_revision,
1079
lossy, *args, **kwargs)
1053
return InterBranch.get(self, target).push(
1054
overwrite, stop_revision, lossy, *args, **kwargs)
1081
1056
def basis_tree(self):
1082
1057
"""Return `Tree` object for last revision."""
1213
1188
if revno < 1 or revno > self.revno():
1214
1189
raise errors.InvalidRevisionNumber(revno)
1216
def clone(self, to_controldir, revision_id=None, repository_policy=None):
1191
def clone(self, to_controldir, revision_id=None, name=None,
1192
repository_policy=None, tag_selector=None):
1217
1193
"""Clone this branch into to_controldir preserving all semantic values.
1219
1195
Most API users will want 'create_clone_on_transport', which creates a
1222
1198
revision_id: if not None, the revision history in the new branch will
1223
1199
be truncated to end with revision_id.
1225
result = to_controldir.create_branch()
1201
result = to_controldir.create_branch(name=name)
1226
1202
with self.lock_read(), result.lock_write():
1227
1203
if repository_policy is not None:
1228
1204
repository_policy.configure_branch(result)
1229
self.copy_content_into(result, revision_id=revision_id)
1205
self.copy_content_into(
1206
result, revision_id=revision_id, tag_selector=tag_selector)
1232
1209
def sprout(self, to_controldir, revision_id=None, repository_policy=None,
1210
repository=None, lossy=False, tag_selector=None):
1234
1211
"""Create a new line of development from the branch, into to_controldir.
1236
1213
to_controldir controls the branch format.
1238
1215
revision_id: if not None, the revision history in the new branch will
1239
1216
be truncated to end with revision_id.
1241
if (repository_policy is not None and
1242
repository_policy.requires_stacking()):
1218
if (repository_policy is not None
1219
and repository_policy.requires_stacking()):
1243
1220
to_controldir._format.require_stacking(_skip_repo=True)
1244
1221
result = to_controldir.create_branch(repository=repository)
1223
raise errors.LossyPushToSameVCS(self, result)
1245
1224
with self.lock_read(), result.lock_write():
1246
1225
if repository_policy is not None:
1247
1226
repository_policy.configure_branch(result)
1248
self.copy_content_into(result, revision_id=revision_id)
1227
self.copy_content_into(
1228
result, revision_id=revision_id, tag_selector=tag_selector)
1249
1229
master_url = self.get_bound_location()
1250
1230
if master_url is None:
1251
result.set_parent(self.controldir.root_transport.base)
1231
result.set_parent(self.user_url)
1253
1233
result.set_parent(master_url)
1272
1252
graph = self.repository.get_graph()
1274
revno = graph.find_distance_to_null(revision_id,
1275
[(source_revision_id, source_revno)])
1254
revno = graph.find_distance_to_null(
1255
revision_id, [(source_revision_id, source_revno)])
1276
1256
except errors.GhostRevisionsHaveNoRevno:
1277
1257
# Default to 1, if we can't find anything else
1279
1259
destination.set_last_revision_info(revno, revision_id)
1281
def copy_content_into(self, destination, revision_id=None):
1261
def copy_content_into(self, destination, revision_id=None, tag_selector=None):
1282
1262
"""Copy the content of self into destination.
1284
1264
revision_id: if not None, the revision history in the new branch will
1285
1265
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
1287
1269
return InterBranch.get(self, destination).copy_content_into(
1288
revision_id=revision_id)
1270
revision_id=revision_id, tag_selector=tag_selector)
1290
1272
def update_references(self, target):
1291
if not getattr(self._format, 'supports_reference_locations', False):
1293
reference_dict = self._get_all_reference_info()
1294
if len(reference_dict) == 0:
1296
old_base = self.base
1297
new_base = target.base
1298
target_reference_dict = target._get_all_reference_info()
1299
for file_id, (tree_path, branch_location) in viewitems(reference_dict):
1300
branch_location = urlutils.rebase_url(branch_location,
1302
target_reference_dict.setdefault(
1303
file_id, (tree_path, branch_location))
1304
target._set_all_reference_info(target_reference_dict)
1273
if not self._format.supports_reference_locations:
1275
return InterBranch.get(self, target).update_references()
1306
1277
def check(self, refs):
1307
1278
"""Check consistency of the branch.
1323
1294
if actual_revno != last_revno:
1324
1295
result.errors.append(errors.BzrCheckError(
1325
1296
'revno does not match len(mainline) %s != %s' % (
1326
last_revno, actual_revno)))
1297
last_revno, actual_revno)))
1327
1298
# TODO: We should probably also check that self.revision_history
1328
1299
# matches the repository for older branch formats.
1329
# If looking for the code that cross-checks repository parents against
1330
# the Graph.iter_lefthand_ancestry output, that is now a repository
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.
1334
1305
def _get_checkout_format(self, lightweight=False):
1355
1327
# XXX: Fix the bzrdir API to allow getting the branch back from the
1356
1328
# clone call. Or something. 20090224 RBC/spiv.
1357
# XXX: Should this perhaps clone colocated branches as well,
1329
# XXX: Should this perhaps clone colocated branches as well,
1358
1330
# rather than just the default branch? 20100319 JRV
1359
1331
if revision_id is None:
1360
1332
revision_id = self.last_revision()
1361
dir_to = self.controldir.clone_on_transport(to_transport,
1362
revision_id=revision_id, stacked_on=stacked_on,
1333
dir_to = self.controldir.clone_on_transport(
1334
to_transport, revision_id=revision_id, stacked_on=stacked_on,
1363
1335
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1336
no_tree=no_tree, tag_selector=tag_selector)
1365
1337
return dir_to.open_branch()
1367
1339
def create_checkout(self, to_location, revision_id=None,
1368
1340
lightweight=False, accelerator_tree=None,
1341
hardlink=False, recurse_nested=True):
1370
1342
"""Create a checkout of a branch.
1372
1344
:param to_location: The url to produce the checkout at
1418
1392
hardlink=hardlink)
1419
1393
basis_tree = tree.basis_tree()
1420
1394
with basis_tree.lock_read():
1421
for path, file_id in basis_tree.iter_references():
1422
reference_parent = self.reference_parent(file_id, path)
1423
reference_parent.create_checkout(tree.abspath(path),
1424
basis_tree.get_reference_revision(path, file_id),
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)
1428
1405
def reconcile(self, thorough=True):
1429
"""Make sure the data stored in this branch is consistent."""
1430
from breezy.reconcile import BranchReconciler
1431
with self.lock_write():
1432
reconciler = BranchReconciler(self, thorough=thorough)
1433
reconciler.reconcile()
1436
def reference_parent(self, file_id, path, possible_transports=None):
1437
"""Return the parent branch for a tree-reference file_id
1439
:param file_id: The file_id of the tree reference
1440
:param path: The path of the file_id in the tree
1441
:return: A branch associated with the file_id
1406
"""Make sure the data stored in this branch is consistent.
1408
:return: A `ReconcileResult` object.
1443
# FIXME should provide multiple branches, based on config
1444
return Branch.open(self.controldir.root_transport.clone(path).base,
1445
possible_transports=possible_transports)
1410
raise NotImplementedError(self.reconcile)
1447
1412
def supports_tags(self):
1448
1413
return self._format.supports_tags()
1682
1658
Hooks.__init__(self, "breezy.branch", "Branch.hooks")
1683
self.add_hook('open',
1684
1661
"Called with the Branch object that has been opened after a "
1685
1662
"branch is opened.", (1, 8))
1686
self.add_hook('post_push',
1687
1665
"Called after a push operation completes. post_push is called "
1688
"with a breezy.branch.BranchPushResult object and only runs in the "
1689
"bzr client.", (0, 15))
1690
self.add_hook('post_pull',
1666
"with a breezy.branch.BranchPushResult object and only runs in "
1667
"the bzr client.", (0, 15))
1691
1670
"Called after a pull operation completes. post_pull is called "
1692
1671
"with a breezy.branch.PullResult object and only runs in the "
1693
1672
"bzr client.", (0, 15))
1694
self.add_hook('pre_commit',
1695
1675
"Called after a commit is calculated but before it is "
1696
1676
"completed. pre_commit is called with (local, master, old_revno, "
1697
1677
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1701
1681
" future_tree is an in-memory tree obtained from "
1702
1682
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1703
1683
"tree.", (0, 91))
1704
self.add_hook('post_commit',
1705
1686
"Called in the bzr client after a commit has completed. "
1706
1687
"post_commit is called with (local, master, old_revno, old_revid, "
1707
1688
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1708
1689
"commit to a branch.", (0, 15))
1709
self.add_hook('post_uncommit',
1710
1692
"Called in the bzr client after an uncommit completes. "
1711
1693
"post_uncommit is called with (local, master, old_revno, "
1712
1694
"old_revid, new_revno, new_revid) where local is the local branch "
1713
1695
"or None, master is the target branch, and an empty branch "
1714
1696
"receives new_revno of 0, new_revid of None.", (0, 15))
1715
self.add_hook('pre_change_branch_tip',
1698
'pre_change_branch_tip',
1716
1699
"Called in bzr client and server before a change to the tip of a "
1717
1700
"branch is made. pre_change_branch_tip is called with a "
1718
1701
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1719
1702
"commit, uncommit will all trigger this hook.", (1, 6))
1720
self.add_hook('post_change_branch_tip',
1704
'post_change_branch_tip',
1721
1705
"Called in bzr client and server after a change to the tip of a "
1722
1706
"branch is made. post_change_branch_tip is called with a "
1723
1707
"breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1724
1708
"commit, uncommit will all trigger this hook.", (1, 4))
1725
self.add_hook('transform_fallback_location',
1710
'transform_fallback_location',
1726
1711
"Called when a stacked branch is activating its fallback "
1727
1712
"locations. transform_fallback_location is called with (branch, "
1728
1713
"url), and should return a new url. Returning the same url "
1734
1719
"multiple hooks installed for transform_fallback_location, "
1735
1720
"all are called with the url returned from the previous hook."
1736
1721
"The order is however undefined.", (1, 9))
1737
self.add_hook('automatic_tag_name',
1723
'automatic_tag_name',
1738
1724
"Called to determine an automatic tag name for a revision. "
1739
1725
"automatic_tag_name is called with (branch, revision_id) and "
1740
1726
"should return a tag name or None if no tag name could be "
1741
1727
"determined. The first non-None tag name returned will be used.",
1743
self.add_hook('post_branch_init',
1744
1731
"Called after new branch initialization completes. "
1745
1732
"post_branch_init is called with a "
1746
1733
"breezy.branch.BranchInitHookParams. "
1747
1734
"Note that init, branch and checkout (both heavyweight and "
1748
1735
"lightweight) will all trigger this hook.", (2, 2))
1749
self.add_hook('post_switch',
1750
1738
"Called after a checkout switches branch. "
1751
1739
"post_switch is called with a "
1752
1740
"breezy.branch.SwitchHookParams.", (2, 2))
1756
1743
# install the default hooks into the Branch class.
1757
1744
Branch.hooks = BranchHooks()
1908
1895
# formats which have no format string are not discoverable
1909
1896
# and not independently creatable, so are not registered.
1910
1897
format_registry.register_lazy(
1911
"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1912
1899
"BzrBranchFormat5")
1913
1900
format_registry.register_lazy(
1914
"Bazaar Branch Format 6 (bzr 0.15)\n",
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1915
1902
"breezy.bzr.branch", "BzrBranchFormat6")
1916
1903
format_registry.register_lazy(
1917
"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1918
1905
"breezy.bzr.branch", "BzrBranchFormat7")
1919
1906
format_registry.register_lazy(
1920
"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1921
1908
"breezy.bzr.branch", "BzrBranchFormat8")
1922
1909
format_registry.register_lazy(
1923
"Bazaar-NG Branch Reference Format 1\n",
1910
b"Bazaar-NG Branch Reference Format 1\n",
1924
1911
"breezy.bzr.branch", "BranchReferenceFormat")
1926
format_registry.set_default_key("Bazaar Branch Format 7 (needs bzr 1.6)\n")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1929
1916
class BranchWriteLockResult(LogicalLockResult):
2012
1999
tag_updates = getattr(self, "tag_updates", None)
2013
2000
if not is_quiet():
2014
2001
if self.old_revid != self.new_revid:
2015
note(gettext('Pushed up to revision %d.') % self.new_revno)
2002
if self.new_revno is not None:
2003
note(gettext('Pushed up to revision %d.'),
2006
note(gettext('Pushed up to revision id %s.'),
2007
self.new_revid.decode('utf-8'))
2016
2008
if tag_updates:
2017
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
2009
note(ngettext('%d tag updated.', '%d tags updated.',
2010
len(tag_updates)) % len(tag_updates))
2018
2011
if self.old_revid == self.new_revid and not tag_updates:
2019
2012
if not tag_conflicts:
2020
2013
note(gettext('No new revisions or tags to push.'))
2077
2070
raise NotImplementedError(self.pull)
2079
2072
def push(self, overwrite=False, stop_revision=None, lossy=False,
2080
_override_hook_source_branch=None):
2073
_override_hook_source_branch=None, tag_selector=None):
2081
2074
"""Mirror the source branch into the target branch.
2083
2076
The source branch is considered to be 'local', having low latency.
2085
2078
raise NotImplementedError(self.push)
2087
def copy_content_into(self, revision_id=None):
2080
def copy_content_into(self, revision_id=None, tag_selector=None):
2088
2081
"""Copy the content of source into target
2090
revision_id: if not None, the revision history in the new branch will
2091
be truncated to end with revision_id.
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.
2093
2089
raise NotImplementedError(self.copy_content_into)
2095
def fetch(self, stop_revision=None, limit=None):
2091
def fetch(self, stop_revision=None, limit=None, lossy=False):
2096
2092
"""Fetch revisions.
2098
2094
:param stop_revision: Last revision to fetch
2099
2095
:param limit: Optional rough limit of revisions to fetch
2096
:return: FetchResult object
2101
2098
raise NotImplementedError(self.fetch)
2100
def update_references(self):
2101
"""Import reference information from source to target.
2103
raise NotImplementedError(self.update_references)
2104
2106
def _fix_overwrite_type(overwrite):
2105
2107
if isinstance(overwrite, bool):
2129
2131
return format._custom_format
2132
def copy_content_into(self, revision_id=None):
2134
def copy_content_into(self, revision_id=None, tag_selector=None):
2133
2135
"""Copy the content of source into target
2135
2137
revision_id: if not None, the revision history in the new branch will
2136
2138
be truncated to end with revision_id.
2138
2140
with self.source.lock_read(), self.target.lock_write():
2139
self.source.update_references(self.target)
2140
2141
self.source._synchronize_history(self.target, revision_id)
2142
self.update_references()
2142
2144
parent = self.source.get_parent()
2143
2145
except errors.InaccessibleParent as e:
2144
mutter('parent was not accessible to copy: %s', e)
2146
mutter('parent was not accessible to copy: %s', str(e))
2147
2149
self.target.set_parent(parent)
2148
2150
if self.source._push_should_merge_tags():
2149
self.source.tags.merge_to(self.target.tags)
2151
self.source.tags.merge_to(self.target.tags, selector=tag_selector)
2151
def fetch(self, stop_revision=None, limit=None):
2153
def fetch(self, stop_revision=None, limit=None, lossy=False):
2152
2154
if self.target.base == self.source.base:
2154
2156
with self.source.lock_read(), self.target.lock_write():
2157
2159
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2158
2160
fetch_spec_factory.source_repo = self.source.repository
2159
2161
fetch_spec_factory.target_repo = self.target.repository
2160
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
2162
fetch_spec_factory.target_repo_kind = (
2163
fetch.TargetRepoKinds.PREEXISTING)
2161
2164
fetch_spec_factory.limit = limit
2162
2165
fetch_spec = fetch_spec_factory.make_fetch_spec()
2163
2166
return self.target.repository.fetch(
2164
self.source.repository,
2165
fetch_spec=fetch_spec)
2167
self.source.repository,
2169
fetch_spec=fetch_spec)
2167
2171
def _update_revisions(self, stop_revision=None, overwrite=False,
2169
2173
with self.source.lock_read(), self.target.lock_write():
2170
2174
other_revno, other_last_revision = self.source.last_revision_info()
2171
stop_revno = None # unknown
2175
stop_revno = None # unknown
2172
2176
if stop_revision is None:
2173
2177
stop_revision = other_last_revision
2174
2178
if _mod_revision.is_null(stop_revision):
2197
2201
if graph is None:
2198
2202
graph = self.target.repository.get_graph()
2199
2203
this_revno, this_last_revision = \
2200
self.target.last_revision_info()
2201
stop_revno = graph.find_distance_to_null(stop_revision,
2202
[(other_last_revision, other_revno),
2203
(this_last_revision, this_revno)])
2204
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)])
2204
2208
self.target.set_last_revision_info(stop_revno, stop_revision)
2206
2210
def pull(self, overwrite=False, stop_revision=None,
2207
2211
possible_transports=None, run_hooks=True,
2208
_override_hook_target=None, local=False):
2212
_override_hook_target=None, local=False,
2209
2214
"""Pull from source into self, updating my master if any.
2211
2216
:param run_hooks: Private parameter - if false, this branch
2212
2217
is being called because it's the master of the primary branch,
2213
2218
so it should not run its hooks.
2215
with self.target.lock_write():
2220
with contextlib.ExitStack() as exit_stack:
2221
exit_stack.enter_context(self.target.lock_write())
2216
2222
bound_location = self.target.get_bound_location()
2217
2223
if local and not bound_location:
2218
2224
raise errors.LocalRequiresBoundBranch()
2229
2235
source_is_master = False
2230
2236
if not local and bound_location and not source_is_master:
2231
2237
# not pulling from master, so we need to update master.
2232
master_branch = self.target.get_master_branch(possible_transports)
2233
master_branch.lock_write()
2236
# pull from source into master.
2237
master_branch.pull(self.source, overwrite, stop_revision,
2239
return self._pull(overwrite,
2240
stop_revision, _hook_master=master_branch,
2241
run_hooks=run_hooks,
2242
_override_hook_target=_override_hook_target,
2243
merge_tags_to_master=not source_is_master)
2246
master_branch.unlock()
2238
master_branch = self.target.get_master_branch(
2239
possible_transports)
2240
exit_stack.enter_context(master_branch.lock_write())
2242
# 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,
2248
run_hooks=run_hooks,
2249
_override_hook_target=_override_hook_target,
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
2248
2253
def push(self, overwrite=False, stop_revision=None, lossy=False,
2249
_override_hook_source_branch=None):
2254
_override_hook_source_branch=None, tag_selector=None):
2250
2255
"""See InterBranch.push.
2252
2257
This is the basic concrete implementation of push()
2261
2266
# TODO: Public option to disable running hooks - should be trivial but
2264
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
2265
op.add_cleanup(self.source.lock_read().unlock)
2266
op.add_cleanup(self.target.lock_write().unlock)
2267
return op.run(overwrite, stop_revision,
2268
_override_hook_source_branch=_override_hook_source_branch)
2270
def _basic_push(self, overwrite, stop_revision):
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):
2271
2310
"""Basic implementation of push without bound branches or hooks.
2273
2312
Must be called with source read locked and target write locked.
2276
2315
result.source_branch = self.source
2277
2316
result.target_branch = self.target
2278
2317
result.old_revno, result.old_revid = self.target.last_revision_info()
2279
self.source.update_references(self.target)
2280
2318
overwrite = _fix_overwrite_type(overwrite)
2281
2319
if result.old_revid != stop_revision:
2282
2320
# We assume that during 'push' this repository is closer than
2284
2322
graph = self.source.repository.get_graph(self.target.repository)
2285
self._update_revisions(stop_revision,
2286
overwrite=("history" in overwrite),
2323
self._update_revisions(
2324
stop_revision, overwrite=("history" in overwrite), graph=graph)
2288
2325
if self.source._push_should_merge_tags():
2289
2326
result.tag_updates, result.tag_conflicts = (
2290
2327
self.source.tags.merge_to(
2291
self.target.tags, "tags" in overwrite))
2328
self.target.tags, "tags" in overwrite, selector=tag_selector))
2329
self.update_references()
2292
2330
result.new_revno, result.new_revid = self.target.last_revision_info()
2295
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
2296
_override_hook_source_branch=None):
2297
"""Push from source into target, and into target's master if any.
2300
if _override_hook_source_branch:
2301
result.source_branch = _override_hook_source_branch
2302
for hook in Branch.hooks['post_push']:
2305
bound_location = self.target.get_bound_location()
2306
if bound_location and self.target.base != bound_location:
2307
# there is a master branch.
2309
# XXX: Why the second check? Is it even supported for a branch to
2310
# be bound to itself? -- mbp 20070507
2311
master_branch = self.target.get_master_branch()
2312
master_branch.lock_write()
2313
operation.add_cleanup(master_branch.unlock)
2314
# push into the master from the source branch.
2315
master_inter = InterBranch.get(self.source, master_branch)
2316
master_inter._basic_push(overwrite, stop_revision)
2317
# and push into the target branch from the source. Note that
2318
# we push from the source branch again, because it's considered
2319
# the highest bandwidth repository.
2320
result = self._basic_push(overwrite, stop_revision)
2321
result.master_branch = master_branch
2322
result.local_branch = self.target
2324
master_branch = None
2326
result = self._basic_push(overwrite, stop_revision)
2327
# TODO: Why set master_branch and local_branch if there's no
2328
# binding? Maybe cleaner to just leave them unset? -- mbp
2330
result.master_branch = self.target
2331
result.local_branch = None
2335
2333
def _pull(self, overwrite=False, stop_revision=None,
2336
possible_transports=None, _hook_master=None, run_hooks=True,
2337
_override_hook_target=None, local=False,
2338
merge_tags_to_master=True):
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):
2339
2337
"""See Branch.pull.
2341
2339
This function is the core worker, used by GenericInterBranch.pull to
2364
2362
with self.source.lock_read():
2365
2363
# We assume that during 'pull' the target repository is closer than
2366
2364
# the source one.
2367
self.source.update_references(self.target)
2368
2365
graph = self.target.repository.get_graph(self.source.repository)
2369
# TODO: Branch formats should have a flag that indicates
2366
# TODO: Branch formats should have a flag that indicates
2370
2367
# that revno's are expensive, and pull() should honor that flag.
2371
2368
# -- JRV20090506
2372
2369
result.old_revno, result.old_revid = \
2373
2370
self.target.last_revision_info()
2374
2371
overwrite = _fix_overwrite_type(overwrite)
2375
self._update_revisions(stop_revision,
2376
overwrite=("history" in overwrite),
2378
# TODO: The old revid should be specified when merging tags,
2379
# so a tags implementation that versions tags can only
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
2380
2376
# pull in the most recent changes. -- JRV20090506
2381
2377
result.tag_updates, result.tag_conflicts = (
2382
self.source.tags.merge_to(self.target.tags,
2383
"tags" in overwrite,
2384
ignore_master=not merge_tags_to_master))
2385
result.new_revno, result.new_revid = self.target.last_revision_info()
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())
2386
2385
if _hook_master:
2387
2386
result.master_branch = _hook_master
2388
2387
result.local_branch = result.target_branch
2396
def update_references(self):
2397
if not getattr(self.source._format, 'supports_reference_locations', False):
2399
reference_dict = self.source._get_all_reference_info()
2400
if len(reference_dict) == 0:
2402
old_base = self.source.base
2403
new_base = self.target.base
2404
target_reference_dict = self.target._get_all_reference_info()
2405
for tree_path, (branch_location, file_id) in reference_dict.items():
2407
branch_location = urlutils.rebase_url(branch_location,
2409
except urlutils.InvalidRebaseURLs:
2410
# Fall back to absolute URL
2411
branch_location = urlutils.join(old_base, branch_location)
2412
target_reference_dict.setdefault(
2413
tree_path, (branch_location, file_id))
2414
self.target._set_all_reference_info(target_reference_dict)
2398
2417
InterBranch.register_optimiser(GenericInterBranch)