66
67
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
70
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
71
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
72
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
69
75
class Branch(controldir.ControlComponent):
70
76
"""Branch holding a history of revisions.
450
456
after. If None, the rest of history is included.
451
457
:param stop_rule: if stop_revision_id is not None, the precise rule
452
458
to use for termination:
454
459
* 'exclude' - leave the stop revision out of the result (default)
455
460
* 'include' - the stop revision is the last item in the result
456
461
* 'with-merges' - include the stop revision and all of its
458
463
* 'with-merges-without-common-ancestry' - filter out revisions
459
464
that are in both ancestries
460
465
:param direction: either 'reverse' or 'forward':
462
466
* reverse means return the start_revision_id first, i.e.
463
467
start at the most recent revision and go backwards in history
464
468
* forward returns tuples in the opposite order to reverse.
520
524
if stop_revision_id is None:
521
525
# Yield everything
522
526
for node in rev_iter:
527
rev_id = node.key[-1]
524
528
yield (rev_id, node.merge_depth, node.revno,
525
529
node.end_of_merge)
526
530
elif stop_rule == 'exclude':
527
531
for node in rev_iter:
532
rev_id = node.key[-1]
529
533
if rev_id == stop_revision_id:
531
535
yield (rev_id, node.merge_depth, node.revno,
532
536
node.end_of_merge)
533
537
elif stop_rule == 'include':
534
538
for node in rev_iter:
539
rev_id = node.key[-1]
536
540
yield (rev_id, node.merge_depth, node.revno,
537
541
node.end_of_merge)
538
542
if rev_id == stop_revision_id:
544
548
ancestors = graph.find_unique_ancestors(start_revision_id,
545
549
[stop_revision_id])
546
550
for node in rev_iter:
551
rev_id = node.key[-1]
548
552
if rev_id not in ancestors:
550
554
yield (rev_id, node.merge_depth, node.revno,
665
669
raise errors.UnsupportedOperation(self.get_reference_info, self)
667
671
@needs_write_lock
668
def fetch(self, from_branch, last_revision=None, limit=None):
672
def fetch(self, from_branch, last_revision=None):
669
673
"""Copy revisions from from_branch into this branch.
671
675
:param from_branch: Where to copy from.
672
676
:param last_revision: What revision to stop at (None for at the end
674
:param limit: Optional rough limit of revisions to fetch
677
return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
680
return InterBranch.get(from_branch, self).fetch(last_revision)
679
682
def get_bound_location(self):
680
683
"""Return the URL of the branch we are bound to.
773
776
configured to check constraints on history, in which case this may not
776
raise NotImplementedError(self.set_last_revision_info)
779
raise NotImplementedError(self.last_revision_info)
778
781
@needs_write_lock
779
782
def generate_revision_history(self, revision_id, last_rev=None,
780
783
other_branch=None):
781
784
"""See Branch.generate_revision_history"""
782
graph = self.repository.get_graph()
783
(last_revno, last_revid) = self.last_revision_info()
784
known_revision_ids = [
785
(last_revid, last_revno),
786
(_mod_revision.NULL_REVISION, 0),
788
if last_rev is not None:
789
if not graph.is_ancestor(last_rev, revision_id):
790
# our previous tip is not merged into stop_revision
791
raise errors.DivergedBranches(self, other_branch)
792
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
785
# FIXME: This shouldn't have to fetch the entire history
786
history = self._lefthand_history(revision_id, last_rev, other_branch)
793
788
self.set_last_revision_info(revno, revision_id)
789
self._cache_revision_history(history)
795
791
@needs_write_lock
796
792
def set_parent(self, url):
929
925
:seealso: Branch._get_tags_bytes.
931
op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
932
op.add_cleanup(self.lock_write().unlock)
933
return op.run_simple(bytes)
927
return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
935
930
def _set_tags_bytes_locked(self, bytes):
936
931
self._tags_bytes = bytes
1103
1098
stop_revision=stop_revision,
1104
1099
possible_transports=possible_transports, *args, **kwargs)
1106
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1101
def push(self, target, overwrite=False, stop_revision=None, *args,
1108
1103
"""Mirror this branch into target.
1110
1105
This branch is considered to be 'local', having low latency.
1112
1107
return InterBranch.get(self, target).push(overwrite, stop_revision,
1113
lossy, *args, **kwargs)
1110
def lossy_push(self, target, stop_revision=None):
1111
"""Push deltas into another branch.
1113
:note: This does not, like push, retain the revision ids from
1114
the source branch and will, rather than adding bzr-specific
1115
metadata, push only those semantics of the revision that can be
1116
natively represented by this branch' VCS.
1118
:param target: Target branch
1119
:param stop_revision: Revision to push, defaults to last revision.
1120
:return: BranchPushResult with an extra member revidmap:
1121
A dictionary mapping revision ids from the target branch
1122
to new revision ids in the target branch, for each
1123
revision that was pushed.
1125
inter = InterBranch.get(self, target)
1126
lossy_push = getattr(inter, "lossy_push", None)
1127
if lossy_push is None:
1128
raise errors.LossyPushToSameVCS(self, target)
1129
return lossy_push(stop_revision)
1115
1131
def basis_tree(self):
1116
1132
"""Return `Tree` object for last revision."""
1289
1305
if repository_policy is not None:
1290
1306
repository_policy.configure_branch(result)
1291
1307
self.copy_content_into(result, revision_id=revision_id)
1292
master_url = self.get_bound_location()
1293
if master_url is None:
1308
master_branch = self.get_master_branch()
1309
if master_branch is None:
1294
1310
result.set_parent(self.bzrdir.root_transport.base)
1296
result.set_parent(master_url)
1312
result.set_parent(master_branch.bzrdir.root_transport.base)
1298
1314
result.unlock()
1418
1434
:param to_location: The url to produce the checkout at
1419
1435
:param revision_id: The revision to check out
1420
1436
:param lightweight: If True, produce a lightweight checkout, otherwise,
1421
produce a bound branch (heavyweight checkout)
1437
produce a bound branch (heavyweight checkout)
1422
1438
:param accelerator_tree: A tree which can be used for retrieving file
1423
1439
contents more quickly than the revision tree, i.e. a workingtree.
1424
1440
The revision tree will be used for cases where accelerator_tree's
1471
1487
def reference_parent(self, file_id, path, possible_transports=None):
1472
1488
"""Return the parent branch for a tree-reference file_id
1474
1489
:param file_id: The file_id of the tree reference
1475
1490
:param path: The path of the file_id in the tree
1476
1491
:return: A branch associated with the file_id
1541
1556
# For bzr native formats must_fetch is just the tip, and if_present_fetch
1542
1557
# are the tags.
1543
1558
must_fetch = set([self.last_revision()])
1544
if_present_fetch = set()
1545
c = self.get_config()
1546
include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1550
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1551
except errors.TagsNotSupported:
1560
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1561
except errors.TagsNotSupported:
1562
if_present_fetch = set()
1553
1563
must_fetch.discard(_mod_revision.NULL_REVISION)
1554
1564
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1555
1565
return must_fetch, if_present_fetch
1873
1883
class ChangeBranchTipParams(object):
1874
"""Object holding parameters passed to `*_change_branch_tip` hooks.
1884
"""Object holding parameters passed to *_change_branch_tip hooks.
1876
1886
There are 5 fields that hooks may wish to access:
1911
1921
class BranchInitHookParams(object):
1912
"""Object holding parameters passed to `*_branch_init` hooks.
1922
"""Object holding parameters passed to *_branch_init hooks.
1914
1924
There are 4 fields that hooks may wish to access:
2798
2808
self._reference_info = None
2800
2810
def _check_history_violation(self, revision_id):
2801
current_revid = self.last_revision()
2802
last_revision = _mod_revision.ensure_null(current_revid)
2811
last_revision = _mod_revision.ensure_null(self.last_revision())
2803
2812
if _mod_revision.is_null(last_revision):
2805
graph = self.repository.get_graph()
2806
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2807
if lh_ancestor == current_revid:
2809
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2814
if last_revision not in self._lefthand_history(revision_id):
2815
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2811
2817
def _gen_revision_history(self):
2812
2818
"""Generate the revision history from last revision
2943
2949
# you can always ask for the URL; but you might not be able to use it
2944
2950
# if the repo can't support stacking.
2945
2951
## self._check_stackable_repo()
2946
# stacked_on_location is only ever defined in branch.conf, so don't
2947
# waste effort reading the whole stack of config files.
2948
config = self.get_config()._get_branch_data_config()
2949
stacked_url = self._get_config_location('stacked_on_location',
2952
stacked_url = self._get_config_location('stacked_on_location')
2951
2953
if stacked_url is None:
2952
2954
raise errors.NotStacked(self)
2953
2955
return stacked_url
3168
3170
class Converter7to8(object):
3169
"""Perform an in-place upgrade of format 7 to format 8"""
3171
"""Perform an in-place upgrade of format 6 to format 7"""
3171
3173
def convert(self, branch):
3172
3174
format = BzrBranchFormat8()
3175
3177
branch._transport.put_bytes('format', format.get_format_string())
3180
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3181
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3184
_run_with_write_locked_target will attempt to release the lock it acquires.
3186
If an exception is raised by callable, then that exception *will* be
3187
propagated, even if the unlock attempt raises its own error. Thus
3188
_run_with_write_locked_target should be preferred to simply doing::
3192
return callable(*args, **kwargs)
3197
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3198
# should share code?
3201
result = callable(*args, **kwargs)
3203
exc_info = sys.exc_info()
3207
raise exc_info[0], exc_info[1], exc_info[2]
3178
3213
class InterBranch(InterObject):
3179
3214
"""This class represents operations taking place between two branches.
3208
3243
raise NotImplementedError(self.pull)
3210
3245
@needs_write_lock
3211
def push(self, overwrite=False, stop_revision=None, lossy=False,
3246
def push(self, overwrite=False, stop_revision=None,
3212
3247
_override_hook_source_branch=None):
3213
3248
"""Mirror the source branch into the target branch.
3226
3261
raise NotImplementedError(self.copy_content_into)
3228
3263
@needs_write_lock
3229
def fetch(self, stop_revision=None, limit=None):
3264
def fetch(self, stop_revision=None):
3230
3265
"""Fetch revisions.
3232
3267
:param stop_revision: Last revision to fetch
3233
:param limit: Optional rough limit of revisions to fetch
3235
3269
raise NotImplementedError(self.fetch)
3274
3308
self.source.tags.merge_to(self.target.tags)
3276
3310
@needs_write_lock
3277
def fetch(self, stop_revision=None, limit=None):
3311
def fetch(self, stop_revision=None):
3278
3312
if self.target.base == self.source.base:
3280
3314
self.source.lock_read()
3285
3319
fetch_spec_factory.source_repo = self.source.repository
3286
3320
fetch_spec_factory.target_repo = self.target.repository
3287
3321
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3288
fetch_spec_factory.limit = limit
3289
3322
fetch_spec = fetch_spec_factory.make_fetch_spec()
3290
3323
return self.target.repository.fetch(self.source.repository,
3291
3324
fetch_spec=fetch_spec)
3345
3378
if local and not bound_location:
3346
3379
raise errors.LocalRequiresBoundBranch()
3347
3380
master_branch = None
3348
source_is_master = False
3350
# bound_location comes from a config file, some care has to be
3351
# taken to relate it to source.user_url
3352
normalized = urlutils.normalize_url(bound_location)
3354
relpath = self.source.user_transport.relpath(normalized)
3355
source_is_master = (relpath == '')
3356
except (errors.PathNotChild, errors.InvalidURL):
3357
source_is_master = False
3381
source_is_master = (self.source.user_url == bound_location)
3358
3382
if not local and bound_location and not source_is_master:
3359
3383
# not pulling from master, so we need to update master.
3360
3384
master_branch = self.target.get_master_branch(possible_transports)
3373
3397
if master_branch:
3374
3398
master_branch.unlock()
3376
def push(self, overwrite=False, stop_revision=None, lossy=False,
3400
def push(self, overwrite=False, stop_revision=None,
3377
3401
_override_hook_source_branch=None):
3378
3402
"""See InterBranch.push.
3380
3404
This is the basic concrete implementation of push()
3382
:param _override_hook_source_branch: If specified, run the hooks
3383
passing this Branch as the source, rather than self. This is for
3384
use of RemoteBranch, where push is delegated to the underlying
3406
:param _override_hook_source_branch: If specified, run
3407
the hooks passing this Branch as the source, rather than self.
3408
This is for use of RemoteBranch, where push is delegated to the
3409
underlying vfs-based Branch.
3388
raise errors.LossyPushToSameVCS(self.source, self.target)
3389
3411
# TODO: Public option to disable running hooks - should be trivial but
3392
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3393
op.add_cleanup(self.source.lock_read().unlock)
3394
op.add_cleanup(self.target.lock_write().unlock)
3395
return op.run(overwrite, stop_revision,
3396
_override_hook_source_branch=_override_hook_source_branch)
3413
self.source.lock_read()
3415
return _run_with_write_locked_target(
3416
self.target, self._push_with_bound_branches, overwrite,
3418
_override_hook_source_branch=_override_hook_source_branch)
3420
self.source.unlock()
3398
3422
def _basic_push(self, overwrite, stop_revision):
3399
3423
"""Basic implementation of push without bound branches or hooks.
3417
3441
result.new_revno, result.new_revid = self.target.last_revision_info()
3420
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3444
def _push_with_bound_branches(self, overwrite, stop_revision,
3421
3445
_override_hook_source_branch=None):
3422
3446
"""Push from source into target, and into target's master if any.
3435
3459
# be bound to itself? -- mbp 20070507
3436
3460
master_branch = self.target.get_master_branch()
3437
3461
master_branch.lock_write()
3438
operation.add_cleanup(master_branch.unlock)
3439
# push into the master from the source branch.
3440
master_inter = InterBranch.get(self.source, master_branch)
3441
master_inter._basic_push(overwrite, stop_revision)
3442
# and push into the target branch from the source. Note that
3443
# we push from the source branch again, because it's considered
3444
# the highest bandwidth repository.
3445
result = self._basic_push(overwrite, stop_revision)
3446
result.master_branch = master_branch
3447
result.local_branch = self.target
3463
# push into the master from the source branch.
3464
master_inter = InterBranch.get(self.source, master_branch)
3465
master_inter._basic_push(overwrite, stop_revision)
3466
# and push into the target branch from the source. Note that
3467
# we push from the source branch again, because it's considered
3468
# the highest bandwidth repository.
3469
result = self._basic_push(overwrite, stop_revision)
3470
result.master_branch = master_branch
3471
result.local_branch = self.target
3475
master_branch.unlock()
3449
master_branch = None
3450
3477
# no master branch
3451
3478
result = self._basic_push(overwrite, stop_revision)
3452
3479
# TODO: Why set master_branch and local_branch if there's no
3455
3482
result.master_branch = self.target
3456
3483
result.local_branch = None
3460
3487
def _pull(self, overwrite=False, stop_revision=None,
3461
3488
possible_transports=None, _hook_master=None, run_hooks=True,