772
806
if len(old_repository._fallback_repositories) != 1:
773
807
raise AssertionError("can't cope with fallback repositories "
774
808
"of %r" % (self.repository,))
775
# unlock it, including unlocking the fallback
809
# Open the new repository object.
810
# Repositories don't offer an interface to remove fallback
811
# repositories today; take the conceptually simpler option and just
812
# reopen it. We reopen it starting from the URL so that we
813
# get a separate connection for RemoteRepositories and can
814
# stream from one of them to the other. This does mean doing
815
# separate SSH connection setup, but unstacking is not a
816
# common operation so it's tolerable.
817
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
818
new_repository = new_bzrdir.find_repository()
819
if new_repository._fallback_repositories:
820
raise AssertionError("didn't expect %r to have "
821
"fallback_repositories"
822
% (self.repository,))
823
# Replace self.repository with the new repository.
824
# Do our best to transfer the lock state (i.e. lock-tokens and
825
# lock count) of self.repository to the new repository.
826
lock_token = old_repository.lock_write().repository_token
827
self.repository = new_repository
828
if isinstance(self, remote.RemoteBranch):
829
# Remote branches can have a second reference to the old
830
# repository that need to be replaced.
831
if self._real_branch is not None:
832
self._real_branch.repository = new_repository
833
self.repository.lock_write(token=lock_token)
834
if lock_token is not None:
835
old_repository.leave_lock_in_place()
776
836
old_repository.unlock()
837
if lock_token is not None:
838
# XXX: self.repository.leave_lock_in_place() before this
839
# function will not be preserved. Fortunately that doesn't
840
# affect the current default format (2a), and would be a
841
# corner-case anyway.
842
# - Andrew Bennetts, 2010/06/30
843
self.repository.dont_leave_lock_in_place()
847
old_repository.unlock()
848
except errors.LockNotHeld:
851
if old_lock_count == 0:
852
raise AssertionError(
853
'old_repository should have been locked at least once.')
854
for i in range(old_lock_count-1):
855
self.repository.lock_write()
856
# Fetch from the old repository into the new.
777
857
old_repository.lock_read()
779
# Repositories don't offer an interface to remove fallback
780
# repositories today; take the conceptually simpler option and just
781
# reopen it. We reopen it starting from the URL so that we
782
# get a separate connection for RemoteRepositories and can
783
# stream from one of them to the other. This does mean doing
784
# separate SSH connection setup, but unstacking is not a
785
# common operation so it's tolerable.
786
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
787
new_repository = new_bzrdir.find_repository()
788
self.repository = new_repository
789
if self.repository._fallback_repositories:
790
raise AssertionError("didn't expect %r to have "
791
"fallback_repositories"
792
% (self.repository,))
793
# this is not paired with an unlock because it's just restoring
794
# the previous state; the lock's released when set_stacked_on_url
796
self.repository.lock_write()
797
859
# XXX: If you unstack a branch while it has a working tree
798
860
# with a pending merge, the pending-merged revisions will no
799
861
# longer be present. You can (probably) revert and remerge.
3170
3310
raise NotImplementedError(self.push)
3313
def copy_content_into(self, revision_id=None):
3314
"""Copy the content of source into target
3316
revision_id: if not None, the revision history in the new branch will
3317
be truncated to end with revision_id.
3319
raise NotImplementedError(self.copy_content_into)
3173
3322
class GenericInterBranch(InterBranch):
3174
"""InterBranch implementation that uses public Branch functions.
3178
def _get_branch_formats_to_test():
3179
return BranchFormat._default_format, BranchFormat._default_format
3323
"""InterBranch implementation that uses public Branch functions."""
3326
def is_compatible(klass, source, target):
3327
# GenericBranch uses the public API, so always compatible
3331
def _get_branch_formats_to_test(klass):
3332
return [(BranchFormat._default_format, BranchFormat._default_format)]
3335
def unwrap_format(klass, format):
3336
if isinstance(format, remote.RemoteBranchFormat):
3337
format._ensure_real()
3338
return format._custom_format
3342
def copy_content_into(self, revision_id=None):
3343
"""Copy the content of source into target
3345
revision_id: if not None, the revision history in the new branch will
3346
be truncated to end with revision_id.
3348
self.source.update_references(self.target)
3349
self.source._synchronize_history(self.target, revision_id)
3351
parent = self.source.get_parent()
3352
except errors.InaccessibleParent, e:
3353
mutter('parent was not accessible to copy: %s', e)
3356
self.target.set_parent(parent)
3357
if self.source._push_should_merge_tags():
3358
self.source.tags.merge_to(self.target.tags)
3181
3361
def update_revisions(self, stop_revision=None, overwrite=False,
3183
3363
"""See InterBranch.update_revisions()."""
3184
self.source.lock_read()
3186
other_revno, other_last_revision = self.source.last_revision_info()
3187
stop_revno = None # unknown
3188
if stop_revision is None:
3189
stop_revision = other_last_revision
3190
if _mod_revision.is_null(stop_revision):
3191
# if there are no commits, we're done.
3193
stop_revno = other_revno
3195
# what's the current last revision, before we fetch [and change it
3197
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3198
# we fetch here so that we don't process data twice in the common
3199
# case of having something to pull, and so that the check for
3200
# already merged can operate on the just fetched graph, which will
3201
# be cached in memory.
3202
self.target.fetch(self.source, stop_revision)
3203
# Check to see if one is an ancestor of the other
3206
graph = self.target.repository.get_graph()
3207
if self.target._check_if_descendant_or_diverged(
3208
stop_revision, last_rev, graph, self.source):
3209
# stop_revision is a descendant of last_rev, but we aren't
3210
# overwriting, so we're done.
3212
if stop_revno is None:
3214
graph = self.target.repository.get_graph()
3215
this_revno, this_last_revision = \
3216
self.target.last_revision_info()
3217
stop_revno = graph.find_distance_to_null(stop_revision,
3218
[(other_last_revision, other_revno),
3219
(this_last_revision, this_revno)])
3220
self.target.set_last_revision_info(stop_revno, stop_revision)
3222
self.source.unlock()
3364
other_revno, other_last_revision = self.source.last_revision_info()
3365
stop_revno = None # unknown
3366
if stop_revision is None:
3367
stop_revision = other_last_revision
3368
if _mod_revision.is_null(stop_revision):
3369
# if there are no commits, we're done.
3371
stop_revno = other_revno
3373
# what's the current last revision, before we fetch [and change it
3375
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3376
# we fetch here so that we don't process data twice in the common
3377
# case of having something to pull, and so that the check for
3378
# already merged can operate on the just fetched graph, which will
3379
# be cached in memory.
3380
self.target.fetch(self.source, stop_revision)
3381
# Check to see if one is an ancestor of the other
3384
graph = self.target.repository.get_graph()
3385
if self.target._check_if_descendant_or_diverged(
3386
stop_revision, last_rev, graph, self.source):
3387
# stop_revision is a descendant of last_rev, but we aren't
3388
# overwriting, so we're done.
3390
if stop_revno is None:
3392
graph = self.target.repository.get_graph()
3393
this_revno, this_last_revision = \
3394
self.target.last_revision_info()
3395
stop_revno = graph.find_distance_to_null(stop_revision,
3396
[(other_last_revision, other_revno),
3397
(this_last_revision, this_revno)])
3398
self.target.set_last_revision_info(stop_revno, stop_revision)
3224
3401
def pull(self, overwrite=False, stop_revision=None,
3225
possible_transports=None, _hook_master=None, run_hooks=True,
3402
possible_transports=None, run_hooks=True,
3226
3403
_override_hook_target=None, local=False):
3404
"""Pull from source into self, updating my master if any.
3229
:param _hook_master: Private parameter - set the branch to
3230
be supplied as the master to pull hooks.
3231
3406
:param run_hooks: Private parameter - if false, this branch
3232
3407
is being called because it's the master of the primary branch,
3233
3408
so it should not run its hooks.
3234
:param _override_hook_target: Private parameter - set the branch to be
3235
supplied as the target_branch to pull hooks.
3236
:param local: Only update the local branch, and not the bound branch.
3238
# This type of branch can't be bound.
3410
bound_location = self.target.get_bound_location()
3411
if local and not bound_location:
3240
3412
raise errors.LocalRequiresBoundBranch()
3241
result = PullResult()
3242
result.source_branch = self.source
3243
if _override_hook_target is None:
3244
result.target_branch = self.target
3246
result.target_branch = _override_hook_target
3247
self.source.lock_read()
3413
master_branch = None
3414
if not local and bound_location and self.source.user_url != bound_location:
3415
# not pulling from master, so we need to update master.
3416
master_branch = self.target.get_master_branch(possible_transports)
3417
master_branch.lock_write()
3249
# We assume that during 'pull' the target repository is closer than
3251
self.source.update_references(self.target)
3252
graph = self.target.repository.get_graph(self.source.repository)
3253
# TODO: Branch formats should have a flag that indicates
3254
# that revno's are expensive, and pull() should honor that flag.
3256
result.old_revno, result.old_revid = \
3257
self.target.last_revision_info()
3258
self.target.update_revisions(self.source, stop_revision,
3259
overwrite=overwrite, graph=graph)
3260
# TODO: The old revid should be specified when merging tags,
3261
# so a tags implementation that versions tags can only
3262
# pull in the most recent changes. -- JRV20090506
3263
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3265
result.new_revno, result.new_revid = self.target.last_revision_info()
3267
result.master_branch = _hook_master
3268
result.local_branch = result.target_branch
3270
result.master_branch = result.target_branch
3271
result.local_branch = None
3273
for hook in Branch.hooks['post_pull']:
3420
# pull from source into master.
3421
master_branch.pull(self.source, overwrite, stop_revision,
3423
return self._pull(overwrite,
3424
stop_revision, _hook_master=master_branch,
3425
run_hooks=run_hooks,
3426
_override_hook_target=_override_hook_target)
3276
self.source.unlock()
3429
master_branch.unlock()
3279
3431
def push(self, overwrite=False, stop_revision=None,
3280
3432
_override_hook_source_branch=None):
3346
def is_compatible(self, source, target):
3347
# GenericBranch uses the public API, so always compatible
3351
class InterToBranch5(GenericInterBranch):
3354
def _get_branch_formats_to_test():
3355
return BranchFormat._default_format, BzrBranchFormat5()
3357
def pull(self, overwrite=False, stop_revision=None,
3358
possible_transports=None, run_hooks=True,
3497
def _pull(self, overwrite=False, stop_revision=None,
3498
possible_transports=None, _hook_master=None, run_hooks=True,
3359
3499
_override_hook_target=None, local=False):
3360
"""Pull from source into self, updating my master if any.
3502
This function is the core worker, used by GenericInterBranch.pull to
3503
avoid duplication when pulling source->master and source->local.
3505
:param _hook_master: Private parameter - set the branch to
3506
be supplied as the master to pull hooks.
3362
3507
:param run_hooks: Private parameter - if false, this branch
3363
3508
is being called because it's the master of the primary branch,
3364
3509
so it should not run its hooks.
3510
:param _override_hook_target: Private parameter - set the branch to be
3511
supplied as the target_branch to pull hooks.
3512
:param local: Only update the local branch, and not the bound branch.
3366
bound_location = self.target.get_bound_location()
3367
if local and not bound_location:
3514
# This type of branch can't be bound.
3368
3516
raise errors.LocalRequiresBoundBranch()
3369
master_branch = None
3370
if not local and bound_location and self.source.base != bound_location:
3371
# not pulling from master, so we need to update master.
3372
master_branch = self.target.get_master_branch(possible_transports)
3373
master_branch.lock_write()
3517
result = PullResult()
3518
result.source_branch = self.source
3519
if _override_hook_target is None:
3520
result.target_branch = self.target
3522
result.target_branch = _override_hook_target
3523
self.source.lock_read()
3376
# pull from source into master.
3377
master_branch.pull(self.source, overwrite, stop_revision,
3379
return super(InterToBranch5, self).pull(overwrite,
3380
stop_revision, _hook_master=master_branch,
3381
run_hooks=run_hooks,
3382
_override_hook_target=_override_hook_target)
3525
# We assume that during 'pull' the target repository is closer than
3527
self.source.update_references(self.target)
3528
graph = self.target.repository.get_graph(self.source.repository)
3529
# TODO: Branch formats should have a flag that indicates
3530
# that revno's are expensive, and pull() should honor that flag.
3532
result.old_revno, result.old_revid = \
3533
self.target.last_revision_info()
3534
self.target.update_revisions(self.source, stop_revision,
3535
overwrite=overwrite, graph=graph)
3536
# TODO: The old revid should be specified when merging tags,
3537
# so a tags implementation that versions tags can only
3538
# pull in the most recent changes. -- JRV20090506
3539
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3541
result.new_revno, result.new_revid = self.target.last_revision_info()
3543
result.master_branch = _hook_master
3544
result.local_branch = result.target_branch
3546
result.master_branch = result.target_branch
3547
result.local_branch = None
3549
for hook in Branch.hooks['post_pull']:
3385
master_branch.unlock()
3552
self.source.unlock()
3388
3556
InterBranch.register_optimiser(GenericInterBranch)
3389
InterBranch.register_optimiser(InterToBranch5)