490
470
"""Iterate over an inclusive range of sorted revisions."""
491
471
rev_iter = iter(merge_sorted_revisions)
492
472
if start_revision_id is not None:
493
for node in rev_iter:
494
rev_id = node.key[-1]
473
for rev_id, depth, revno, end_of_merge in rev_iter:
495
474
if rev_id != start_revision_id:
498
477
# The decision to include the start or not
499
478
# depends on the stop_rule if a stop is provided
500
# so pop this node back into the iterator
501
rev_iter = chain(iter([node]), rev_iter)
480
iter([(rev_id, depth, revno, end_of_merge)]),
503
483
if stop_revision_id is None:
505
for node in rev_iter:
506
rev_id = node.key[-1]
507
yield (rev_id, node.merge_depth, node.revno,
484
for rev_id, depth, revno, end_of_merge in rev_iter:
485
yield rev_id, depth, revno, end_of_merge
509
486
elif stop_rule == 'exclude':
510
for node in rev_iter:
511
rev_id = node.key[-1]
487
for rev_id, depth, revno, end_of_merge in rev_iter:
512
488
if rev_id == stop_revision_id:
514
yield (rev_id, node.merge_depth, node.revno,
490
yield rev_id, depth, revno, end_of_merge
516
491
elif stop_rule == 'include':
517
for node in rev_iter:
518
rev_id = node.key[-1]
519
yield (rev_id, node.merge_depth, node.revno,
492
for rev_id, depth, revno, end_of_merge in rev_iter:
493
yield rev_id, depth, revno, end_of_merge
521
494
if rev_id == stop_revision_id:
523
elif stop_rule == 'with-merges-without-common-ancestry':
524
# We want to exclude all revisions that are already part of the
525
# stop_revision_id ancestry.
526
graph = self.repository.get_graph()
527
ancestors = graph.find_unique_ancestors(start_revision_id,
529
for node in rev_iter:
530
rev_id = node.key[-1]
531
if rev_id not in ancestors:
533
yield (rev_id, node.merge_depth, node.revno,
535
496
elif stop_rule == 'with-merges':
536
497
stop_rev = self.repository.get_revision(stop_revision_id)
537
498
if stop_rev.parent_ids:
538
499
left_parent = stop_rev.parent_ids[0]
540
501
left_parent = _mod_revision.NULL_REVISION
541
# left_parent is the actual revision we want to stop logging at,
542
# since we want to show the merged revisions after the stop_rev too
543
reached_stop_revision_id = False
544
revision_id_whitelist = []
545
for node in rev_iter:
546
rev_id = node.key[-1]
502
for rev_id, depth, revno, end_of_merge in rev_iter:
547
503
if rev_id == left_parent:
548
# reached the left parent after the stop_revision
550
if (not reached_stop_revision_id or
551
rev_id in revision_id_whitelist):
552
yield (rev_id, node.merge_depth, node.revno,
554
if reached_stop_revision_id or rev_id == stop_revision_id:
555
# only do the merged revs of rev_id from now on
556
rev = self.repository.get_revision(rev_id)
558
reached_stop_revision_id = True
559
revision_id_whitelist.extend(rev.parent_ids)
505
yield rev_id, depth, revno, end_of_merge
561
507
raise ValueError('invalid stop_rule %r' % stop_rule)
563
def _filter_start_non_ancestors(self, rev_iter):
564
# If we started from a dotted revno, we want to consider it as a tip
565
# and don't want to yield revisions that are not part of its
566
# ancestry. Given the order guaranteed by the merge sort, we will see
567
# uninteresting descendants of the first parent of our tip before the
569
first = rev_iter.next()
570
(rev_id, merge_depth, revno, end_of_merge) = first
573
# We start at a mainline revision so by definition, all others
574
# revisions in rev_iter are ancestors
575
for node in rev_iter:
580
pmap = self.repository.get_parent_map([rev_id])
581
parents = pmap.get(rev_id, [])
583
whitelist.update(parents)
585
# If there is no parents, there is nothing of interest left
587
# FIXME: It's hard to test this scenario here as this code is never
588
# called in that case. -- vila 20100322
591
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
593
if rev_id in whitelist:
594
pmap = self.repository.get_parent_map([rev_id])
595
parents = pmap.get(rev_id, [])
596
whitelist.remove(rev_id)
597
whitelist.update(parents)
599
# We've reached the mainline, there is nothing left to
603
# A revision that is not part of the ancestry of our
606
yield (rev_id, merge_depth, revno, end_of_merge)
608
509
def leave_lock_in_place(self):
609
510
"""Tell this branch object not to release the physical lock when this
610
511
object is unlocked.
805
706
if len(old_repository._fallback_repositories) != 1:
806
707
raise AssertionError("can't cope with fallback repositories "
807
708
"of %r" % (self.repository,))
808
# Open the new repository object.
809
# Repositories don't offer an interface to remove fallback
810
# repositories today; take the conceptually simpler option and just
811
# reopen it. We reopen it starting from the URL so that we
812
# get a separate connection for RemoteRepositories and can
813
# stream from one of them to the other. This does mean doing
814
# separate SSH connection setup, but unstacking is not a
815
# common operation so it's tolerable.
816
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
817
new_repository = new_bzrdir.find_repository()
818
if new_repository._fallback_repositories:
819
raise AssertionError("didn't expect %r to have "
820
"fallback_repositories"
821
% (self.repository,))
822
# Replace self.repository with the new repository.
823
# Do our best to transfer the lock state (i.e. lock-tokens and
824
# lock count) of self.repository to the new repository.
825
lock_token = old_repository.lock_write().repository_token
826
self.repository = new_repository
827
if isinstance(self, remote.RemoteBranch):
828
# Remote branches can have a second reference to the old
829
# repository that need to be replaced.
830
if self._real_branch is not None:
831
self._real_branch.repository = new_repository
832
self.repository.lock_write(token=lock_token)
833
if lock_token is not None:
834
old_repository.leave_lock_in_place()
709
# unlock it, including unlocking the fallback
835
710
old_repository.unlock()
836
if lock_token is not None:
837
# XXX: self.repository.leave_lock_in_place() before this
838
# function will not be preserved. Fortunately that doesn't
839
# affect the current default format (2a), and would be a
840
# corner-case anyway.
841
# - Andrew Bennetts, 2010/06/30
842
self.repository.dont_leave_lock_in_place()
846
old_repository.unlock()
847
except errors.LockNotHeld:
850
if old_lock_count == 0:
851
raise AssertionError(
852
'old_repository should have been locked at least once.')
853
for i in range(old_lock_count-1):
711
old_repository.lock_read()
713
# Repositories don't offer an interface to remove fallback
714
# repositories today; take the conceptually simpler option and just
715
# reopen it. We reopen it starting from the URL so that we
716
# get a separate connection for RemoteRepositories and can
717
# stream from one of them to the other. This does mean doing
718
# separate SSH connection setup, but unstacking is not a
719
# common operation so it's tolerable.
720
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
721
new_repository = new_bzrdir.find_repository()
722
self.repository = new_repository
723
if self.repository._fallback_repositories:
724
raise AssertionError("didn't expect %r to have "
725
"fallback_repositories"
726
% (self.repository,))
727
# this is not paired with an unlock because it's just restoring
728
# the previous state; the lock's released when set_stacked_on_url
854
730
self.repository.lock_write()
855
# Fetch from the old repository into the new.
856
old_repository.lock_read()
858
731
# XXX: If you unstack a branch while it has a working tree
859
732
# with a pending merge, the pending-merged revisions will no
860
733
# longer be present. You can (probably) revert and remerge.
1613
1476
"""Return the short format description for this format."""
1614
1477
raise NotImplementedError(self.get_format_description)
1616
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1617
hooks = Branch.hooks['post_branch_init']
1620
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1624
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1625
lock_type='metadir', set_format=True):
1479
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1626
1481
"""Initialize a branch in a bzrdir, with specified files
1628
1483
:param a_bzrdir: The bzrdir to initialize the branch in
1629
1484
:param utf8_files: The files to create as a list of
1630
1485
(filename, content) tuples
1631
:param name: Name of colocated branch to create, if any
1632
1486
:param set_format: If True, set the format with
1633
1487
self.get_format_string. (BzrBranch4 has its format set
1635
1489
:return: a branch in this format
1637
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1638
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1491
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1492
branch_transport = a_bzrdir.get_branch_transport(self)
1640
1494
'metadir': ('lock', lockdir.LockDir),
1641
1495
'branch4': ('branch-lock', lockable_files.TransportLock),
1921
1716
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1924
class BranchInitHookParams(object):
1925
"""Object holding parameters passed to *_branch_init hooks.
1927
There are 4 fields that hooks may wish to access:
1929
:ivar format: the branch format
1930
:ivar bzrdir: the BzrDir where the branch will be/has been initialized
1931
:ivar name: name of colocated branch, if any (or None)
1932
:ivar branch: the branch created
1934
Note that for lightweight checkouts, the bzrdir and format fields refer to
1935
the checkout, hence they are different from the corresponding fields in
1936
branch, which refer to the original branch.
1939
def __init__(self, format, a_bzrdir, name, branch):
1940
"""Create a group of BranchInitHook parameters.
1942
:param format: the branch format
1943
:param a_bzrdir: the BzrDir where the branch will be/has been
1945
:param name: name of colocated branch, if any (or None)
1946
:param branch: the branch created
1948
Note that for lightweight checkouts, the bzrdir and format fields refer
1949
to the checkout, hence they are different from the corresponding fields
1950
in branch, which refer to the original branch.
1952
self.format = format
1953
self.bzrdir = a_bzrdir
1955
self.branch = branch
1957
def __eq__(self, other):
1958
return self.__dict__ == other.__dict__
1961
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1964
class SwitchHookParams(object):
1965
"""Object holding parameters passed to *_switch hooks.
1967
There are 4 fields that hooks may wish to access:
1969
:ivar control_dir: BzrDir of the checkout to change
1970
:ivar to_branch: branch that the checkout is to reference
1971
:ivar force: skip the check for local commits in a heavy checkout
1972
:ivar revision_id: revision ID to switch to (or None)
1975
def __init__(self, control_dir, to_branch, force, revision_id):
1976
"""Create a group of SwitchHook parameters.
1978
:param control_dir: BzrDir of the checkout to change
1979
:param to_branch: branch that the checkout is to reference
1980
:param force: skip the check for local commits in a heavy checkout
1981
:param revision_id: revision ID to switch to (or None)
1983
self.control_dir = control_dir
1984
self.to_branch = to_branch
1986
self.revision_id = revision_id
1988
def __eq__(self, other):
1989
return self.__dict__ == other.__dict__
1992
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1993
self.control_dir, self.to_branch,
1997
1719
class BzrBranchFormat4(BranchFormat):
1998
1720
"""Bzr branch format 4.
2247
1967
"""See BranchFormat.get_format_description()."""
2248
1968
return "Checkout reference format 1"
2250
def get_reference(self, a_bzrdir, name=None):
1970
def get_reference(self, a_bzrdir):
2251
1971
"""See BranchFormat.get_reference()."""
2252
transport = a_bzrdir.get_branch_transport(None, name=name)
2253
return transport.get_bytes('location')
1972
transport = a_bzrdir.get_branch_transport(None)
1973
return transport.get('location').read()
2255
def set_reference(self, a_bzrdir, name, to_branch):
1975
def set_reference(self, a_bzrdir, to_branch):
2256
1976
"""See BranchFormat.set_reference()."""
2257
transport = a_bzrdir.get_branch_transport(None, name=name)
1977
transport = a_bzrdir.get_branch_transport(None)
2258
1978
location = transport.put_bytes('location', to_branch.base)
2260
def initialize(self, a_bzrdir, name=None, target_branch=None):
1980
def initialize(self, a_bzrdir, target_branch=None):
2261
1981
"""Create a branch of this format in a_bzrdir."""
2262
1982
if target_branch is None:
2263
1983
# this format does not implement branch itself, thus the implicit
2264
1984
# creation contract must see it as uninitializable
2265
1985
raise errors.UninitializableFormat(self)
2266
mutter('creating branch reference in %s', a_bzrdir.user_url)
2267
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1986
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1987
branch_transport = a_bzrdir.get_branch_transport(self)
2268
1988
branch_transport.put_bytes('location',
2269
target_branch.bzrdir.user_url)
1989
target_branch.bzrdir.root_transport.base)
2270
1990
branch_transport.put_bytes('format', self.get_format_string())
2272
a_bzrdir, name, _found=True,
1992
a_bzrdir, _found=True,
2273
1993
possible_transports=[target_branch.bzrdir.root_transport])
2274
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2277
1995
def __init__(self):
2278
1996
super(BranchReferenceFormat, self).__init__()
3309
2974
raise NotImplementedError(self.push)
3312
def copy_content_into(self, revision_id=None):
3313
"""Copy the content of source into target
3315
revision_id: if not None, the revision history in the new branch will
3316
be truncated to end with revision_id.
3318
raise NotImplementedError(self.copy_content_into)
3321
2977
class GenericInterBranch(InterBranch):
3322
"""InterBranch implementation that uses public Branch functions."""
3325
def is_compatible(klass, source, target):
3326
# GenericBranch uses the public API, so always compatible
3330
def _get_branch_formats_to_test(klass):
3331
return [(BranchFormat._default_format, BranchFormat._default_format)]
3334
def unwrap_format(klass, format):
3335
if isinstance(format, remote.RemoteBranchFormat):
3336
format._ensure_real()
3337
return format._custom_format
3341
def copy_content_into(self, revision_id=None):
3342
"""Copy the content of source into target
3344
revision_id: if not None, the revision history in the new branch will
3345
be truncated to end with revision_id.
3347
self.source.update_references(self.target)
3348
self.source._synchronize_history(self.target, revision_id)
3350
parent = self.source.get_parent()
3351
except errors.InaccessibleParent, e:
3352
mutter('parent was not accessible to copy: %s', e)
3355
self.target.set_parent(parent)
3356
if self.source._push_should_merge_tags():
3357
self.source.tags.merge_to(self.target.tags)
2978
"""InterBranch implementation that uses public Branch functions.
2982
def _get_branch_formats_to_test():
2983
return BranchFormat._default_format, BranchFormat._default_format
3360
2985
def update_revisions(self, stop_revision=None, overwrite=False,
3362
2987
"""See InterBranch.update_revisions()."""
3363
other_revno, other_last_revision = self.source.last_revision_info()
3364
stop_revno = None # unknown
3365
if stop_revision is None:
3366
stop_revision = other_last_revision
3367
if _mod_revision.is_null(stop_revision):
3368
# if there are no commits, we're done.
3370
stop_revno = other_revno
3372
# what's the current last revision, before we fetch [and change it
3374
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3375
# we fetch here so that we don't process data twice in the common
3376
# case of having something to pull, and so that the check for
3377
# already merged can operate on the just fetched graph, which will
3378
# be cached in memory.
3379
self.target.fetch(self.source, stop_revision)
3380
# Check to see if one is an ancestor of the other
3383
graph = self.target.repository.get_graph()
3384
if self.target._check_if_descendant_or_diverged(
3385
stop_revision, last_rev, graph, self.source):
3386
# stop_revision is a descendant of last_rev, but we aren't
3387
# overwriting, so we're done.
3389
if stop_revno is None:
3391
graph = self.target.repository.get_graph()
3392
this_revno, this_last_revision = \
3393
self.target.last_revision_info()
3394
stop_revno = graph.find_distance_to_null(stop_revision,
3395
[(other_last_revision, other_revno),
3396
(this_last_revision, this_revno)])
3397
self.target.set_last_revision_info(stop_revno, stop_revision)
2988
self.source.lock_read()
2990
other_revno, other_last_revision = self.source.last_revision_info()
2991
stop_revno = None # unknown
2992
if stop_revision is None:
2993
stop_revision = other_last_revision
2994
if _mod_revision.is_null(stop_revision):
2995
# if there are no commits, we're done.
2997
stop_revno = other_revno
2999
# what's the current last revision, before we fetch [and change it
3001
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3002
# we fetch here so that we don't process data twice in the common
3003
# case of having something to pull, and so that the check for
3004
# already merged can operate on the just fetched graph, which will
3005
# be cached in memory.
3006
self.target.fetch(self.source, stop_revision)
3007
# Check to see if one is an ancestor of the other
3010
graph = self.target.repository.get_graph()
3011
if self.target._check_if_descendant_or_diverged(
3012
stop_revision, last_rev, graph, self.source):
3013
# stop_revision is a descendant of last_rev, but we aren't
3014
# overwriting, so we're done.
3016
if stop_revno is None:
3018
graph = self.target.repository.get_graph()
3019
this_revno, this_last_revision = \
3020
self.target.last_revision_info()
3021
stop_revno = graph.find_distance_to_null(stop_revision,
3022
[(other_last_revision, other_revno),
3023
(this_last_revision, this_revno)])
3024
self.target.set_last_revision_info(stop_revno, stop_revision)
3026
self.source.unlock()
3400
3028
def pull(self, overwrite=False, stop_revision=None,
3401
possible_transports=None, run_hooks=True,
3029
possible_transports=None, _hook_master=None, run_hooks=True,
3402
3030
_override_hook_target=None, local=False):
3403
"""Pull from source into self, updating my master if any.
3033
:param _hook_master: Private parameter - set the branch to
3034
be supplied as the master to pull hooks.
3405
3035
:param run_hooks: Private parameter - if false, this branch
3406
3036
is being called because it's the master of the primary branch,
3407
3037
so it should not run its hooks.
3038
:param _override_hook_target: Private parameter - set the branch to be
3039
supplied as the target_branch to pull hooks.
3040
:param local: Only update the local branch, and not the bound branch.
3409
bound_location = self.target.get_bound_location()
3410
if local and not bound_location:
3042
# This type of branch can't be bound.
3411
3044
raise errors.LocalRequiresBoundBranch()
3412
master_branch = None
3413
if not local and bound_location and self.source.user_url != bound_location:
3414
# not pulling from master, so we need to update master.
3415
master_branch = self.target.get_master_branch(possible_transports)
3416
master_branch.lock_write()
3045
result = PullResult()
3046
result.source_branch = self.source
3047
if _override_hook_target is None:
3048
result.target_branch = self.target
3050
result.target_branch = _override_hook_target
3051
self.source.lock_read()
3419
# pull from source into master.
3420
master_branch.pull(self.source, overwrite, stop_revision,
3422
return self._pull(overwrite,
3423
stop_revision, _hook_master=master_branch,
3424
run_hooks=run_hooks,
3425
_override_hook_target=_override_hook_target)
3053
# We assume that during 'pull' the target repository is closer than
3055
self.source.update_references(self.target)
3056
graph = self.target.repository.get_graph(self.source.repository)
3057
# TODO: Branch formats should have a flag that indicates
3058
# that revno's are expensive, and pull() should honor that flag.
3060
result.old_revno, result.old_revid = \
3061
self.target.last_revision_info()
3062
self.target.update_revisions(self.source, stop_revision,
3063
overwrite=overwrite, graph=graph)
3064
# TODO: The old revid should be specified when merging tags,
3065
# so a tags implementation that versions tags can only
3066
# pull in the most recent changes. -- JRV20090506
3067
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3069
result.new_revno, result.new_revid = self.target.last_revision_info()
3071
result.master_branch = _hook_master
3072
result.local_branch = result.target_branch
3074
result.master_branch = result.target_branch
3075
result.local_branch = None
3077
for hook in Branch.hooks['post_pull']:
3428
master_branch.unlock()
3080
self.source.unlock()
3430
3083
def push(self, overwrite=False, stop_revision=None,
3431
3084
_override_hook_source_branch=None):
3496
def _pull(self, overwrite=False, stop_revision=None,
3497
possible_transports=None, _hook_master=None, run_hooks=True,
3150
def is_compatible(self, source, target):
3151
# GenericBranch uses the public API, so always compatible
3155
class InterToBranch5(GenericInterBranch):
3158
def _get_branch_formats_to_test():
3159
return BranchFormat._default_format, BzrBranchFormat5()
3161
def pull(self, overwrite=False, stop_revision=None,
3162
possible_transports=None, run_hooks=True,
3498
3163
_override_hook_target=None, local=False):
3501
This function is the core worker, used by GenericInterBranch.pull to
3502
avoid duplication when pulling source->master and source->local.
3504
:param _hook_master: Private parameter - set the branch to
3505
be supplied as the master to pull hooks.
3164
"""Pull from source into self, updating my master if any.
3506
3166
:param run_hooks: Private parameter - if false, this branch
3507
3167
is being called because it's the master of the primary branch,
3508
3168
so it should not run its hooks.
3509
:param _override_hook_target: Private parameter - set the branch to be
3510
supplied as the target_branch to pull hooks.
3511
:param local: Only update the local branch, and not the bound branch.
3513
# This type of branch can't be bound.
3170
bound_location = self.target.get_bound_location()
3171
if local and not bound_location:
3515
3172
raise errors.LocalRequiresBoundBranch()
3516
result = PullResult()
3517
result.source_branch = self.source
3518
if _override_hook_target is None:
3519
result.target_branch = self.target
3521
result.target_branch = _override_hook_target
3522
self.source.lock_read()
3173
master_branch = None
3174
if not local and bound_location and self.source.base != bound_location:
3175
# not pulling from master, so we need to update master.
3176
master_branch = self.target.get_master_branch(possible_transports)
3177
master_branch.lock_write()
3524
# We assume that during 'pull' the target repository is closer than
3526
self.source.update_references(self.target)
3527
graph = self.target.repository.get_graph(self.source.repository)
3528
# TODO: Branch formats should have a flag that indicates
3529
# that revno's are expensive, and pull() should honor that flag.
3531
result.old_revno, result.old_revid = \
3532
self.target.last_revision_info()
3533
self.target.update_revisions(self.source, stop_revision,
3534
overwrite=overwrite, graph=graph)
3535
# TODO: The old revid should be specified when merging tags,
3536
# so a tags implementation that versions tags can only
3537
# pull in the most recent changes. -- JRV20090506
3538
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3540
result.new_revno, result.new_revid = self.target.last_revision_info()
3542
result.master_branch = _hook_master
3543
result.local_branch = result.target_branch
3545
result.master_branch = result.target_branch
3546
result.local_branch = None
3548
for hook in Branch.hooks['post_pull']:
3180
# pull from source into master.
3181
master_branch.pull(self.source, overwrite, stop_revision,
3183
return super(InterToBranch5, self).pull(overwrite,
3184
stop_revision, _hook_master=master_branch,
3185
run_hooks=run_hooks,
3186
_override_hook_target=_override_hook_target)
3551
self.source.unlock()
3189
master_branch.unlock()
3555
3192
InterBranch.register_optimiser(GenericInterBranch)
3193
InterBranch.register_optimiser(InterToBranch5)