782
825
old_repository = self.repository
783
826
if len(old_repository._fallback_repositories) != 1:
784
827
raise AssertionError("can't cope with fallback repositories "
785
"of %r" % (self.repository,))
786
# unlock it, including unlocking the fallback
828
"of %r (fallbacks: %r)" % (old_repository,
829
old_repository._fallback_repositories))
830
# Open the new repository object.
831
# Repositories don't offer an interface to remove fallback
832
# repositories today; take the conceptually simpler option and just
833
# reopen it. We reopen it starting from the URL so that we
834
# get a separate connection for RemoteRepositories and can
835
# stream from one of them to the other. This does mean doing
836
# separate SSH connection setup, but unstacking is not a
837
# common operation so it's tolerable.
838
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
839
new_repository = new_bzrdir.find_repository()
840
if new_repository._fallback_repositories:
841
raise AssertionError("didn't expect %r to have "
842
"fallback_repositories"
843
% (self.repository,))
844
# Replace self.repository with the new repository.
845
# Do our best to transfer the lock state (i.e. lock-tokens and
846
# lock count) of self.repository to the new repository.
847
lock_token = old_repository.lock_write().repository_token
848
self.repository = new_repository
849
if isinstance(self, remote.RemoteBranch):
850
# Remote branches can have a second reference to the old
851
# repository that need to be replaced.
852
if self._real_branch is not None:
853
self._real_branch.repository = new_repository
854
self.repository.lock_write(token=lock_token)
855
if lock_token is not None:
856
old_repository.leave_lock_in_place()
787
857
old_repository.unlock()
858
if lock_token is not None:
859
# XXX: self.repository.leave_lock_in_place() before this
860
# function will not be preserved. Fortunately that doesn't
861
# affect the current default format (2a), and would be a
862
# corner-case anyway.
863
# - Andrew Bennetts, 2010/06/30
864
self.repository.dont_leave_lock_in_place()
868
old_repository.unlock()
869
except errors.LockNotHeld:
872
if old_lock_count == 0:
873
raise AssertionError(
874
'old_repository should have been locked at least once.')
875
for i in range(old_lock_count-1):
876
self.repository.lock_write()
877
# Fetch from the old repository into the new.
788
878
old_repository.lock_read()
790
# Repositories don't offer an interface to remove fallback
791
# repositories today; take the conceptually simpler option and just
792
# reopen it. We reopen it starting from the URL so that we
793
# get a separate connection for RemoteRepositories and can
794
# stream from one of them to the other. This does mean doing
795
# separate SSH connection setup, but unstacking is not a
796
# common operation so it's tolerable.
797
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
798
new_repository = new_bzrdir.find_repository()
799
self.repository = new_repository
800
if self.repository._fallback_repositories:
801
raise AssertionError("didn't expect %r to have "
802
"fallback_repositories"
803
% (self.repository,))
804
# this is not paired with an unlock because it's just restoring
805
# the previous state; the lock's released when set_stacked_on_url
807
self.repository.lock_write()
808
880
# XXX: If you unstack a branch while it has a working tree
809
881
# with a pending merge, the pending-merged revisions will no
810
882
# longer be present. You can (probably) revert and remerge.
812
# XXX: This only fetches up to the tip of the repository; it
813
# doesn't bring across any tags. That's fairly consistent
814
# with how branch works, but perhaps not ideal.
815
self.repository.fetch(old_repository,
816
revision_id=self.last_revision(),
884
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
885
except errors.TagsNotSupported:
886
tags_to_fetch = set()
887
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
888
old_repository, required_ids=[self.last_revision()],
889
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
890
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
819
892
old_repository.unlock()
1565
1675
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1566
lock_type='metadir', set_format=True):
1567
1677
"""Initialize a branch in a bzrdir, with specified files
1569
1679
:param a_bzrdir: The bzrdir to initialize the branch in
1570
1680
:param utf8_files: The files to create as a list of
1571
1681
(filename, content) tuples
1572
1682
:param name: Name of colocated branch to create, if any
1573
:param set_format: If True, set the format with
1574
self.get_format_string. (BzrBranch4 has its format set
1576
1683
:return: a branch in this format
1578
1685
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1579
1686
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1581
'metadir': ('lock', lockdir.LockDir),
1582
'branch4': ('branch-lock', lockable_files.TransportLock),
1584
lock_name, lock_class = lock_map[lock_type]
1585
1687
control_files = lockable_files.LockableFiles(branch_transport,
1586
lock_name, lock_class)
1688
'lock', lockdir.LockDir)
1587
1689
control_files.create_lock()
1690
control_files.lock_write()
1589
control_files.lock_write()
1590
except errors.LockContention:
1591
if lock_type != 'branch4':
1597
1692
utf8_files += [('format', self.get_format_string())]
1599
1693
for (filename, content) in utf8_files:
1600
1694
branch_transport.put_bytes(
1601
1695
filename, content,
1602
1696
mode=a_bzrdir._get_file_mode())
1605
control_files.unlock()
1606
branch = self.open(a_bzrdir, name, _found=True)
1698
control_files.unlock()
1699
branch = self.open(a_bzrdir, name, _found=True,
1700
found_repository=repository)
1607
1701
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1610
def initialize(self, a_bzrdir, name=None):
1704
def initialize(self, a_bzrdir, name=None, repository=None):
1611
1705
"""Create a branch of this format in a_bzrdir.
1613
1707
:param name: Name of the colocated branch to create.
3182
3341
raise NotImplementedError(self.push)
3344
def copy_content_into(self, revision_id=None):
3345
"""Copy the content of source into target
3347
revision_id: if not None, the revision history in the new branch will
3348
be truncated to end with revision_id.
3350
raise NotImplementedError(self.copy_content_into)
3185
3353
class GenericInterBranch(InterBranch):
3186
"""InterBranch implementation that uses public Branch functions.
3190
def _get_branch_formats_to_test():
3191
return BranchFormat._default_format, BranchFormat._default_format
3354
"""InterBranch implementation that uses public Branch functions."""
3357
def is_compatible(klass, source, target):
3358
# GenericBranch uses the public API, so always compatible
3362
def _get_branch_formats_to_test(klass):
3363
return [(BranchFormat._default_format, BranchFormat._default_format)]
3366
def unwrap_format(klass, format):
3367
if isinstance(format, remote.RemoteBranchFormat):
3368
format._ensure_real()
3369
return format._custom_format
3373
def copy_content_into(self, revision_id=None):
3374
"""Copy the content of source into target
3376
revision_id: if not None, the revision history in the new branch will
3377
be truncated to end with revision_id.
3379
self.source.update_references(self.target)
3380
self.source._synchronize_history(self.target, revision_id)
3382
parent = self.source.get_parent()
3383
except errors.InaccessibleParent, e:
3384
mutter('parent was not accessible to copy: %s', e)
3387
self.target.set_parent(parent)
3388
if self.source._push_should_merge_tags():
3389
self.source.tags.merge_to(self.target.tags)
3193
3392
def update_revisions(self, stop_revision=None, overwrite=False,
3393
graph=None, fetch_tags=True):
3195
3394
"""See InterBranch.update_revisions()."""
3196
self.source.lock_read()
3198
other_revno, other_last_revision = self.source.last_revision_info()
3199
stop_revno = None # unknown
3200
if stop_revision is None:
3201
stop_revision = other_last_revision
3202
if _mod_revision.is_null(stop_revision):
3203
# if there are no commits, we're done.
3205
stop_revno = other_revno
3207
# what's the current last revision, before we fetch [and change it
3209
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3210
# we fetch here so that we don't process data twice in the common
3211
# case of having something to pull, and so that the check for
3212
# already merged can operate on the just fetched graph, which will
3213
# be cached in memory.
3214
self.target.fetch(self.source, stop_revision)
3215
# Check to see if one is an ancestor of the other
3218
graph = self.target.repository.get_graph()
3219
if self.target._check_if_descendant_or_diverged(
3220
stop_revision, last_rev, graph, self.source):
3221
# stop_revision is a descendant of last_rev, but we aren't
3222
# overwriting, so we're done.
3224
if stop_revno is None:
3226
graph = self.target.repository.get_graph()
3227
this_revno, this_last_revision = \
3228
self.target.last_revision_info()
3229
stop_revno = graph.find_distance_to_null(stop_revision,
3230
[(other_last_revision, other_revno),
3231
(this_last_revision, this_revno)])
3232
self.target.set_last_revision_info(stop_revno, stop_revision)
3234
self.source.unlock()
3395
other_revno, other_last_revision = self.source.last_revision_info()
3396
stop_revno = None # unknown
3397
if stop_revision is None:
3398
stop_revision = other_last_revision
3399
if _mod_revision.is_null(stop_revision):
3400
# if there are no commits, we're done.
3402
stop_revno = other_revno
3404
# what's the current last revision, before we fetch [and change it
3406
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3407
# we fetch here so that we don't process data twice in the common
3408
# case of having something to pull, and so that the check for
3409
# already merged can operate on the just fetched graph, which will
3410
# be cached in memory.
3412
fetch_spec_factory = fetch.FetchSpecFactory()
3413
fetch_spec_factory.source_branch = self.source
3414
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3415
fetch_spec_factory.source_repo = self.source.repository
3416
fetch_spec_factory.target_repo = self.target.repository
3417
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3418
fetch_spec = fetch_spec_factory.make_fetch_spec()
3420
fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
3421
self.source.repository, revision_ids=[stop_revision]).execute()
3422
self.target.fetch(self.source, fetch_spec=fetch_spec)
3423
# Check to see if one is an ancestor of the other
3426
graph = self.target.repository.get_graph()
3427
if self.target._check_if_descendant_or_diverged(
3428
stop_revision, last_rev, graph, self.source):
3429
# stop_revision is a descendant of last_rev, but we aren't
3430
# overwriting, so we're done.
3432
if stop_revno is None:
3434
graph = self.target.repository.get_graph()
3435
this_revno, this_last_revision = \
3436
self.target.last_revision_info()
3437
stop_revno = graph.find_distance_to_null(stop_revision,
3438
[(other_last_revision, other_revno),
3439
(this_last_revision, this_revno)])
3440
self.target.set_last_revision_info(stop_revno, stop_revision)
3236
3443
def pull(self, overwrite=False, stop_revision=None,
3237
possible_transports=None, _hook_master=None, run_hooks=True,
3444
possible_transports=None, run_hooks=True,
3238
3445
_override_hook_target=None, local=False):
3446
"""Pull from source into self, updating my master if any.
3241
:param _hook_master: Private parameter - set the branch to
3242
be supplied as the master to pull hooks.
3243
3448
:param run_hooks: Private parameter - if false, this branch
3244
3449
is being called because it's the master of the primary branch,
3245
3450
so it should not run its hooks.
3246
:param _override_hook_target: Private parameter - set the branch to be
3247
supplied as the target_branch to pull hooks.
3248
:param local: Only update the local branch, and not the bound branch.
3250
# This type of branch can't be bound.
3452
bound_location = self.target.get_bound_location()
3453
if local and not bound_location:
3252
3454
raise errors.LocalRequiresBoundBranch()
3253
result = PullResult()
3254
result.source_branch = self.source
3255
if _override_hook_target is None:
3256
result.target_branch = self.target
3258
result.target_branch = _override_hook_target
3259
self.source.lock_read()
3455
master_branch = None
3456
source_is_master = (self.source.user_url == bound_location)
3457
if not local and bound_location and not source_is_master:
3458
# not pulling from master, so we need to update master.
3459
master_branch = self.target.get_master_branch(possible_transports)
3460
master_branch.lock_write()
3261
# We assume that during 'pull' the target repository is closer than
3263
self.source.update_references(self.target)
3264
graph = self.target.repository.get_graph(self.source.repository)
3265
# TODO: Branch formats should have a flag that indicates
3266
# that revno's are expensive, and pull() should honor that flag.
3268
result.old_revno, result.old_revid = \
3269
self.target.last_revision_info()
3270
self.target.update_revisions(self.source, stop_revision,
3271
overwrite=overwrite, graph=graph)
3272
# TODO: The old revid should be specified when merging tags,
3273
# so a tags implementation that versions tags can only
3274
# pull in the most recent changes. -- JRV20090506
3275
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3277
result.new_revno, result.new_revid = self.target.last_revision_info()
3279
result.master_branch = _hook_master
3280
result.local_branch = result.target_branch
3282
result.master_branch = result.target_branch
3283
result.local_branch = None
3285
for hook in Branch.hooks['post_pull']:
3463
# pull from source into master.
3464
master_branch.pull(self.source, overwrite, stop_revision,
3466
return self._pull(overwrite,
3467
stop_revision, _hook_master=master_branch,
3468
run_hooks=run_hooks,
3469
_override_hook_target=_override_hook_target,
3470
merge_tags_to_master=not source_is_master)
3288
self.source.unlock()
3473
master_branch.unlock()
3291
3475
def push(self, overwrite=False, stop_revision=None,
3292
3476
_override_hook_source_branch=None):
3358
def is_compatible(self, source, target):
3359
# GenericBranch uses the public API, so always compatible
3363
class InterToBranch5(GenericInterBranch):
3366
def _get_branch_formats_to_test():
3367
return BranchFormat._default_format, BzrBranchFormat5()
3369
def pull(self, overwrite=False, stop_revision=None,
3370
possible_transports=None, run_hooks=True,
3371
_override_hook_target=None, local=False):
3372
"""Pull from source into self, updating my master if any.
3541
def _pull(self, overwrite=False, stop_revision=None,
3542
possible_transports=None, _hook_master=None, run_hooks=True,
3543
_override_hook_target=None, local=False,
3544
merge_tags_to_master=True):
3547
This function is the core worker, used by GenericInterBranch.pull to
3548
avoid duplication when pulling source->master and source->local.
3550
:param _hook_master: Private parameter - set the branch to
3551
be supplied as the master to pull hooks.
3374
3552
:param run_hooks: Private parameter - if false, this branch
3375
3553
is being called because it's the master of the primary branch,
3376
3554
so it should not run its hooks.
3555
:param _override_hook_target: Private parameter - set the branch to be
3556
supplied as the target_branch to pull hooks.
3557
:param local: Only update the local branch, and not the bound branch.
3378
bound_location = self.target.get_bound_location()
3379
if local and not bound_location:
3559
# This type of branch can't be bound.
3380
3561
raise errors.LocalRequiresBoundBranch()
3381
master_branch = None
3382
if not local and bound_location and self.source.user_url != bound_location:
3383
# not pulling from master, so we need to update master.
3384
master_branch = self.target.get_master_branch(possible_transports)
3385
master_branch.lock_write()
3562
result = PullResult()
3563
result.source_branch = self.source
3564
if _override_hook_target is None:
3565
result.target_branch = self.target
3567
result.target_branch = _override_hook_target
3568
self.source.lock_read()
3388
# pull from source into master.
3389
master_branch.pull(self.source, overwrite, stop_revision,
3391
return super(InterToBranch5, self).pull(overwrite,
3392
stop_revision, _hook_master=master_branch,
3393
run_hooks=run_hooks,
3394
_override_hook_target=_override_hook_target)
3570
# We assume that during 'pull' the target repository is closer than
3572
self.source.update_references(self.target)
3573
graph = self.target.repository.get_graph(self.source.repository)
3574
# TODO: Branch formats should have a flag that indicates
3575
# that revno's are expensive, and pull() should honor that flag.
3577
result.old_revno, result.old_revid = \
3578
self.target.last_revision_info()
3579
self.target.update_revisions(self.source, stop_revision,
3580
overwrite=overwrite, graph=graph)
3581
# TODO: The old revid should be specified when merging tags,
3582
# so a tags implementation that versions tags can only
3583
# pull in the most recent changes. -- JRV20090506
3584
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3585
overwrite, ignore_master=not merge_tags_to_master)
3586
result.new_revno, result.new_revid = self.target.last_revision_info()
3588
result.master_branch = _hook_master
3589
result.local_branch = result.target_branch
3591
result.master_branch = result.target_branch
3592
result.local_branch = None
3594
for hook in Branch.hooks['post_pull']:
3397
master_branch.unlock()
3597
self.source.unlock()
3400
3601
InterBranch.register_optimiser(GenericInterBranch)
3401
InterBranch.register_optimiser(InterToBranch5)