129
180
possible_transports)
130
181
return control.open_branch(), relpath
183
def _push_should_merge_tags(self):
184
"""Should _basic_push merge this branch's tags into the target?
186
The default implementation returns False if this branch has no tags,
187
and True the rest of the time. Subclasses may override this.
189
return self.supports_tags() and self.tags.get_tag_dict()
132
191
def get_config(self):
133
192
return BranchConfig(self)
136
return self.get_config().get_nickname()
194
def _get_config(self):
195
"""Get the concrete config for just the config in this branch.
197
This is not intended for client use; see Branch.get_config for the
202
:return: An object supporting get_option and set_option.
204
raise NotImplementedError(self._get_config)
206
def _get_fallback_repository(self, url):
207
"""Get the repository we fallback to at url."""
208
url = urlutils.join(self.base, url)
209
a_bzrdir = bzrdir.BzrDir.open(url,
210
possible_transports=[self.bzrdir.root_transport])
211
return a_bzrdir.open_branch().repository
213
def _get_tags_bytes(self):
214
"""Get the bytes of a serialised tags dict.
216
Note that not all branches support tags, nor do all use the same tags
217
logic: this method is specific to BasicTags. Other tag implementations
218
may use the same method name and behave differently, safely, because
219
of the double-dispatch via
220
format.make_tags->tags_instance->get_tags_dict.
222
:return: The bytes of the tags file.
223
:seealso: Branch._set_tags_bytes.
225
return self._transport.get_bytes('tags')
227
def _get_nick(self, local=False, possible_transports=None):
228
config = self.get_config()
229
# explicit overrides master, but don't look for master if local is True
230
if not local and not config.has_explicit_nickname():
232
master = self.get_master_branch(possible_transports)
233
if master is not None:
234
# return the master branch value
236
except errors.BzrError, e:
237
# Silently fall back to local implicit nick if the master is
239
mutter("Could not connect to bound branch, "
240
"falling back to local nick.\n " + str(e))
241
return config.get_nickname()
138
243
def _set_nick(self, nick):
139
244
self.get_config().set_user_option('nickname', nick, warn_masked=True)
160
292
raise NotImplementedError(self.get_physical_lock_status)
295
def dotted_revno_to_revision_id(self, revno, _cache_reverse=False):
296
"""Return the revision_id for a dotted revno.
298
:param revno: a tuple like (1,) or (1,1,2)
299
:param _cache_reverse: a private parameter enabling storage
300
of the reverse mapping in a top level cache. (This should
301
only be done in selective circumstances as we want to
302
avoid having the mapping cached multiple times.)
303
:return: the revision_id
304
:raises errors.NoSuchRevision: if the revno doesn't exist
306
rev_id = self._do_dotted_revno_to_revision_id(revno)
308
self._partial_revision_id_to_revno_cache[rev_id] = revno
311
def _do_dotted_revno_to_revision_id(self, revno):
312
"""Worker function for dotted_revno_to_revision_id.
314
Subclasses should override this if they wish to
315
provide a more efficient implementation.
318
return self.get_rev_id(revno[0])
319
revision_id_to_revno = self.get_revision_id_to_revno_map()
320
revision_ids = [revision_id for revision_id, this_revno
321
in revision_id_to_revno.iteritems()
322
if revno == this_revno]
323
if len(revision_ids) == 1:
324
return revision_ids[0]
326
revno_str = '.'.join(map(str, revno))
327
raise errors.NoSuchRevision(self, revno_str)
330
def revision_id_to_dotted_revno(self, revision_id):
331
"""Given a revision id, return its dotted revno.
333
:return: a tuple like (1,) or (400,1,3).
335
return self._do_revision_id_to_dotted_revno(revision_id)
337
def _do_revision_id_to_dotted_revno(self, revision_id):
338
"""Worker function for revision_id_to_revno."""
339
# Try the caches if they are loaded
340
result = self._partial_revision_id_to_revno_cache.get(revision_id)
341
if result is not None:
343
if self._revision_id_to_revno_cache:
344
result = self._revision_id_to_revno_cache.get(revision_id)
346
raise errors.NoSuchRevision(self, revision_id)
347
# Try the mainline as it's optimised
349
revno = self.revision_id_to_revno(revision_id)
351
except errors.NoSuchRevision:
352
# We need to load and use the full revno map after all
353
result = self.get_revision_id_to_revno_map().get(revision_id)
355
raise errors.NoSuchRevision(self, revision_id)
163
359
def get_revision_id_to_revno_map(self):
164
360
"""Return the revision_id => dotted revno map.
190
386
:return: A dictionary mapping revision_id => dotted revno.
192
last_revision = self.last_revision()
193
revision_graph = repository._old_get_graph(self.repository,
195
merge_sorted_revisions = tsort.merge_sort(
200
388
revision_id_to_revno = dict((rev_id, revno)
201
for seq_num, rev_id, depth, revno, end_of_merge
202
in merge_sorted_revisions)
389
for rev_id, depth, revno, end_of_merge
390
in self.iter_merge_sorted_revisions())
203
391
return revision_id_to_revno
394
def iter_merge_sorted_revisions(self, start_revision_id=None,
395
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
396
"""Walk the revisions for a branch in merge sorted order.
398
Merge sorted order is the output from a merge-aware,
399
topological sort, i.e. all parents come before their
400
children going forward; the opposite for reverse.
402
:param start_revision_id: the revision_id to begin walking from.
403
If None, the branch tip is used.
404
:param stop_revision_id: the revision_id to terminate the walk
405
after. If None, the rest of history is included.
406
:param stop_rule: if stop_revision_id is not None, the precise rule
407
to use for termination:
408
* 'exclude' - leave the stop revision out of the result (default)
409
* 'include' - the stop revision is the last item in the result
410
* 'with-merges' - include the stop revision and all of its
411
merged revisions in the result
412
:param direction: either 'reverse' or 'forward':
413
* reverse means return the start_revision_id first, i.e.
414
start at the most recent revision and go backwards in history
415
* forward returns tuples in the opposite order to reverse.
416
Note in particular that forward does *not* do any intelligent
417
ordering w.r.t. depth as some clients of this API may like.
418
(If required, that ought to be done at higher layers.)
420
:return: an iterator over (revision_id, depth, revno, end_of_merge)
423
* revision_id: the unique id of the revision
424
* depth: How many levels of merging deep this node has been
426
* revno_sequence: This field provides a sequence of
427
revision numbers for all revisions. The format is:
428
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
429
branch that the revno is on. From left to right the REVNO numbers
430
are the sequence numbers within that branch of the revision.
431
* end_of_merge: When True the next node (earlier in history) is
432
part of a different merge.
434
# Note: depth and revno values are in the context of the branch so
435
# we need the full graph to get stable numbers, regardless of the
437
if self._merge_sorted_revisions_cache is None:
438
last_revision = self.last_revision()
439
graph = self.repository.get_graph()
440
parent_map = dict(((key, value) for key, value in
441
graph.iter_ancestry([last_revision]) if value is not None))
442
revision_graph = repository._strip_NULL_ghosts(parent_map)
443
revs = tsort.merge_sort(revision_graph, last_revision, None,
445
# Drop the sequence # before caching
446
self._merge_sorted_revisions_cache = [r[1:] for r in revs]
448
filtered = self._filter_merge_sorted_revisions(
449
self._merge_sorted_revisions_cache, start_revision_id,
450
stop_revision_id, stop_rule)
451
if direction == 'reverse':
453
if direction == 'forward':
454
return reversed(list(filtered))
456
raise ValueError('invalid direction %r' % direction)
458
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
459
start_revision_id, stop_revision_id, stop_rule):
460
"""Iterate over an inclusive range of sorted revisions."""
461
rev_iter = iter(merge_sorted_revisions)
462
if start_revision_id is not None:
463
for rev_id, depth, revno, end_of_merge in rev_iter:
464
if rev_id != start_revision_id:
467
# The decision to include the start or not
468
# depends on the stop_rule if a stop is provided
470
iter([(rev_id, depth, revno, end_of_merge)]),
473
if stop_revision_id is None:
474
for rev_id, depth, revno, end_of_merge in rev_iter:
475
yield rev_id, depth, revno, end_of_merge
476
elif stop_rule == 'exclude':
477
for rev_id, depth, revno, end_of_merge in rev_iter:
478
if rev_id == stop_revision_id:
480
yield rev_id, depth, revno, end_of_merge
481
elif stop_rule == 'include':
482
for rev_id, depth, revno, end_of_merge in rev_iter:
483
yield rev_id, depth, revno, end_of_merge
484
if rev_id == stop_revision_id:
486
elif stop_rule == 'with-merges':
487
stop_rev = self.repository.get_revision(stop_revision_id)
488
if stop_rev.parent_ids:
489
left_parent = stop_rev.parent_ids[0]
491
left_parent = _mod_revision.NULL_REVISION
492
for rev_id, depth, revno, end_of_merge in rev_iter:
493
if rev_id == left_parent:
495
yield rev_id, depth, revno, end_of_merge
497
raise ValueError('invalid stop_rule %r' % stop_rule)
205
499
def leave_lock_in_place(self):
206
500
"""Tell this branch object not to release the physical lock when this
207
501
object is unlocked.
209
503
If lock_write doesn't return a token, then this method is not supported.
211
505
self.control_files.leave_in_place()
326
632
def set_revision_history(self, rev_history):
327
633
raise NotImplementedError(self.set_revision_history)
636
def set_parent(self, url):
637
"""See Branch.set_parent."""
638
# TODO: Maybe delete old location files?
639
# URLs should never be unicode, even on the local fs,
640
# FIXUP this and get_parent in a future branch format bump:
641
# read and rewrite the file. RBC 20060125
643
if isinstance(url, unicode):
645
url = url.encode('ascii')
646
except UnicodeEncodeError:
647
raise errors.InvalidURL(url,
648
"Urls must be 7-bit ascii, "
649
"use bzrlib.urlutils.escape")
650
url = urlutils.relative_url(self.base, url)
651
self._set_parent_location(url)
654
def set_stacked_on_url(self, url):
655
"""Set the URL this branch is stacked against.
657
:raises UnstackableBranchFormat: If the branch does not support
659
:raises UnstackableRepositoryFormat: If the repository does not support
662
if not self._format.supports_stacking():
663
raise errors.UnstackableBranchFormat(self._format, self.base)
664
self._check_stackable_repo()
667
old_url = self.get_stacked_on_url()
668
except (errors.NotStacked, errors.UnstackableBranchFormat,
669
errors.UnstackableRepositoryFormat):
672
# XXX: Lock correctness - should unlock our old repo if we were
674
# repositories don't offer an interface to remove fallback
675
# repositories today; take the conceptually simpler option and just
677
self.repository = self.bzrdir.find_repository()
678
self.repository.lock_write()
679
# for every revision reference the branch has, ensure it is pulled
681
source_repository = self._get_fallback_repository(old_url)
682
for revision_id in chain([self.last_revision()],
683
self.tags.get_reverse_tag_dict()):
684
self.repository.fetch(source_repository, revision_id,
687
self._activate_fallback_location(url)
688
# write this out after the repository is stacked to avoid setting a
689
# stacked config that doesn't work.
690
self._set_config_location('stacked_on_location', url)
693
def _set_tags_bytes(self, bytes):
694
"""Mirror method for _get_tags_bytes.
696
:seealso: Branch._get_tags_bytes.
698
return _run_with_write_locked_target(self, self._transport.put_bytes,
329
701
def _cache_revision_history(self, rev_history):
330
702
"""Set the cached revision history to rev_history.
461
861
except ValueError:
462
862
raise errors.NoSuchRevision(self, revision_id)
464
865
def get_rev_id(self, revno, history=None):
465
866
"""Find the revision id of the specified revno."""
467
868
return _mod_revision.NULL_REVISION
469
history = self.revision_history()
470
if revno <= 0 or revno > len(history):
869
last_revno, last_revid = self.last_revision_info()
870
if revno == last_revno:
872
if revno <= 0 or revno > last_revno:
471
873
raise errors.NoSuchRevision(self, revno)
472
return history[revno - 1]
874
distance_from_last = last_revno - revno
875
if len(self._partial_revision_history_cache) <= distance_from_last:
876
self._extend_partial_history(distance_from_last)
877
return self._partial_revision_history_cache[distance_from_last]
474
880
def pull(self, source, overwrite=False, stop_revision=None,
475
possible_transports=None):
881
possible_transports=None, *args, **kwargs):
476
882
"""Mirror source into this branch.
478
884
This branch is considered to be 'local', having low latency.
480
886
:returns: PullResult instance
482
raise NotImplementedError(self.pull)
888
return InterBranch.get(source, self).pull(overwrite=overwrite,
889
stop_revision=stop_revision,
890
possible_transports=possible_transports, *args, **kwargs)
484
def push(self, target, overwrite=False, stop_revision=None):
892
def push(self, target, overwrite=False, stop_revision=None, *args,
485
894
"""Mirror this branch into target.
487
896
This branch is considered to be 'local', having low latency.
489
raise NotImplementedError(self.push)
898
return InterBranch.get(self, target).push(overwrite, stop_revision,
901
def lossy_push(self, target, stop_revision=None):
902
"""Push deltas into another branch.
904
:note: This does not, like push, retain the revision ids from
905
the source branch and will, rather than adding bzr-specific
906
metadata, push only those semantics of the revision that can be
907
natively represented by this branch' VCS.
909
:param target: Target branch
910
:param stop_revision: Revision to push, defaults to last revision.
911
:return: BranchPushResult with an extra member revidmap:
912
A dictionary mapping revision ids from the target branch
913
to new revision ids in the target branch, for each
914
revision that was pushed.
916
inter = InterBranch.get(self, target)
917
lossy_push = getattr(inter, "lossy_push", None)
918
if lossy_push is None:
919
raise errors.LossyPushToSameVCS(self, target)
920
return lossy_push(stop_revision)
491
922
def basis_tree(self):
492
923
"""Return `Tree` object for last revision."""
493
924
return self.repository.revision_tree(self.last_revision())
495
def rename_one(self, from_rel, to_rel):
498
This can change the directory or the filename or both.
500
raise NotImplementedError(self.rename_one)
502
def move(self, from_paths, to_name):
505
to_name must exist as a versioned directory.
507
If to_name exists and is a directory, the files are moved into
508
it, keeping their old names. If it is a directory,
510
Note that to_name is only the last component of the new name;
511
this doesn't change the directory.
513
This returns a list of (from_path, to_path) pairs for each
516
raise NotImplementedError(self.move)
518
926
def get_parent(self):
519
927
"""Return the parent location of the branch.
521
This is the default location for push/pull/missing. The usual
929
This is the default location for pull/missing. The usual
522
930
pattern is that the user can override it by specifying a
525
raise NotImplementedError(self.get_parent)
933
parent = self._get_parent_location()
936
# This is an old-format absolute path to a local branch
938
if parent.startswith('/'):
939
parent = urlutils.local_path_to_url(parent.decode('utf8'))
941
return urlutils.join(self.base[:-1], parent)
942
except errors.InvalidURLJoin, e:
943
raise errors.InaccessibleParent(parent, self.base)
945
def _get_parent_location(self):
946
raise NotImplementedError(self._get_parent_location)
527
948
def _set_config_location(self, name, url, config=None,
528
949
make_relative=False):
614
1066
raise errors.InvalidRevisionNumber(revno)
616
1068
@needs_read_lock
617
def clone(self, to_bzrdir, revision_id=None):
1069
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
618
1070
"""Clone this branch into to_bzrdir preserving all semantic values.
1072
Most API users will want 'create_clone_on_transport', which creates a
1073
new bzrdir and branch on the fly.
620
1075
revision_id: if not None, the revision history in the new branch will
621
1076
be truncated to end with revision_id.
623
result = self._format.initialize(to_bzrdir)
624
self.copy_content_into(result, revision_id=revision_id)
1078
result = to_bzrdir.create_branch()
1081
if repository_policy is not None:
1082
repository_policy.configure_branch(result)
1083
self.copy_content_into(result, revision_id=revision_id)
627
1088
@needs_read_lock
628
def sprout(self, to_bzrdir, revision_id=None):
1089
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
629
1090
"""Create a new line of development from the branch, into to_bzrdir.
1092
to_bzrdir controls the branch format.
631
1094
revision_id: if not None, the revision history in the new branch will
632
1095
be truncated to end with revision_id.
634
result = self._format.initialize(to_bzrdir)
635
self.copy_content_into(result, revision_id=revision_id)
636
result.set_parent(self.bzrdir.root_transport.base)
1097
result = to_bzrdir.create_branch()
1100
if repository_policy is not None:
1101
repository_policy.configure_branch(result)
1102
self.copy_content_into(result, revision_id=revision_id)
1103
result.set_parent(self.bzrdir.root_transport.base)
639
1108
def _synchronize_history(self, destination, revision_id):
640
1109
"""Synchronize last revision and revision history between branches.
642
1111
This version is most efficient when the destination is also a
643
BzrBranch5, but works for BzrBranch6 as long as the revision
644
history is the true lefthand parent history, and all of the revisions
645
are in the destination's repository. If not, set_revision_history
1112
BzrBranch6, but works for BzrBranch5, as long as the destination's
1113
repository contains all the lefthand ancestors of the intended
1114
last_revision. If not, set_last_revision_info will fail.
648
1116
:param destination: The branch to copy the history into
649
1117
:param revision_id: The revision-id to truncate history at. May
650
1118
be None to copy complete history.
652
if revision_id == _mod_revision.NULL_REVISION:
654
new_history = self.revision_history()
655
if revision_id is not None and new_history != []:
1120
source_revno, source_revision_id = self.last_revision_info()
1121
if revision_id is None:
1122
revno, revision_id = source_revno, source_revision_id
1124
graph = self.repository.get_graph()
657
new_history = new_history[:new_history.index(revision_id) + 1]
659
rev = self.repository.get_revision(revision_id)
660
new_history = rev.get_history(self.repository)[1:]
661
destination.set_revision_history(new_history)
1126
revno = graph.find_distance_to_null(revision_id,
1127
[(source_revision_id, source_revno)])
1128
except errors.GhostRevisionsHaveNoRevno:
1129
# Default to 1, if we can't find anything else
1131
destination.set_last_revision_info(revno, revision_id)
663
1133
@needs_read_lock
664
1134
def copy_content_into(self, destination, revision_id=None):
786
1311
reconciler.reconcile()
787
1312
return reconciler
789
def reference_parent(self, file_id, path):
1314
def reference_parent(self, file_id, path, possible_transports=None):
790
1315
"""Return the parent branch for a tree-reference file_id
791
1316
:param file_id: The file_id of the tree reference
792
1317
:param path: The path of the file_id in the tree
793
1318
:return: A branch associated with the file_id
795
1320
# FIXME should provide multiple branches, based on config
796
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1321
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1322
possible_transports=possible_transports)
798
1324
def supports_tags(self):
799
1325
return self._format.supports_tags()
1327
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1329
"""Ensure that revision_b is a descendant of revision_a.
1331
This is a helper function for update_revisions.
1333
:raises: DivergedBranches if revision_b has diverged from revision_a.
1334
:returns: True if revision_b is a descendant of revision_a.
1336
relation = self._revision_relations(revision_a, revision_b, graph)
1337
if relation == 'b_descends_from_a':
1339
elif relation == 'diverged':
1340
raise errors.DivergedBranches(self, other_branch)
1341
elif relation == 'a_descends_from_b':
1344
raise AssertionError("invalid relation: %r" % (relation,))
1346
def _revision_relations(self, revision_a, revision_b, graph):
1347
"""Determine the relationship between two revisions.
1349
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1351
heads = graph.heads([revision_a, revision_b])
1352
if heads == set([revision_b]):
1353
return 'b_descends_from_a'
1354
elif heads == set([revision_a, revision_b]):
1355
# These branches have diverged
1357
elif heads == set([revision_a]):
1358
return 'a_descends_from_b'
1360
raise AssertionError("invalid heads: %r" % (heads,))
802
1363
class BranchFormat(object):
803
1364
"""An encapsulation of the initialization and open routines for a format.
919
1492
"""Is this format supported?
921
1494
Supported formats can be initialized and opened.
922
Unsupported formats may not support initialization or committing or
1495
Unsupported formats may not support initialization or committing or
923
1496
some other features depending on the reason for not being supported.
927
def open(self, a_bzrdir, _found=False):
1500
def make_tags(self, branch):
1501
"""Create a tags object for branch.
1503
This method is on BranchFormat, because BranchFormats are reflected
1504
over the wire via network_name(), whereas full Branch instances require
1505
multiple VFS method calls to operate at all.
1507
The default implementation returns a disabled-tags instance.
1509
Note that it is normal for branch to be a RemoteBranch when using tags
1512
return DisabledTags(branch)
1514
def network_name(self):
1515
"""A simple byte string uniquely identifying this format for RPC calls.
1517
MetaDir branch formats use their disk format string to identify the
1518
repository over the wire. All in one formats such as bzr < 0.8, and
1519
foreign formats like svn/git and hg should use some marker which is
1520
unique and immutable.
1522
raise NotImplementedError(self.network_name)
1524
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
928
1525
"""Return the branch object for a_bzrdir
930
_found is a private parameter, do not use it. It is used to indicate
931
if format probing has already be done.
1527
:param a_bzrdir: A BzrDir that contains a branch.
1528
:param _found: a private parameter, do not use it. It is used to
1529
indicate if format probing has already be done.
1530
:param ignore_fallbacks: when set, no fallback branches will be opened
1531
(if there are any). Default is to open fallbacks.
933
1533
raise NotImplementedError(self.open)
936
1536
def register_format(klass, format):
1537
"""Register a metadir format."""
937
1538
klass._formats[format.get_format_string()] = format
1539
# Metadir formats have a network name of their format string, and get
1540
# registered as class factories.
1541
network_format_registry.register(format.get_format_string(), format.__class__)
940
1544
def set_default_format(klass, format):
941
1545
klass._default_format = format
1547
def supports_set_append_revisions_only(self):
1548
"""True if this format supports set_append_revisions_only."""
1551
def supports_stacking(self):
1552
"""True if this format records a stacked-on branch."""
944
1556
def unregister_format(klass, format):
945
1557
del klass._formats[format.get_format_string()]
947
1559
def __str__(self):
948
return self.get_format_string().rstrip()
1560
return self.get_format_description().rstrip()
950
1562
def supports_tags(self):
951
1563
"""True if this format supports tags stored in the branch"""
952
1564
return False # by default
954
# XXX: Probably doesn't really belong here -- mbp 20070212
955
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
957
branch_transport = a_bzrdir.get_branch_transport(self)
958
control_files = lockable_files.LockableFiles(branch_transport,
959
lock_filename, lock_class)
960
control_files.create_lock()
961
control_files.lock_write()
963
for filename, content in utf8_files:
964
control_files.put_utf8(filename, content)
966
control_files.unlock()
969
1567
class BranchHooks(Hooks):
970
1568
"""A dictionary mapping hook name to a list of callables for branch hooks.
972
1570
e.g. ['set_rh'] Is the list of items to be called when the
973
1571
set_revision_history function is invoked.
982
1580
Hooks.__init__(self)
983
# Introduced in 0.15:
984
# invoked whenever the revision history has been set
985
# with set_revision_history. The api signature is
986
# (branch, revision_history), and the branch will
989
# invoked after a push operation completes.
990
# the api signature is
992
# containing the members
993
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
994
# where local is the local target branch or None, master is the target
995
# master branch, and the rest should be self explanatory. The source
996
# is read locked and the target branches write locked. Source will
997
# be the local low-latency branch.
998
self['post_push'] = []
999
# invoked after a pull operation completes.
1000
# the api signature is
1002
# containing the members
1003
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1004
# where local is the local branch or None, master is the target
1005
# master branch, and the rest should be self explanatory. The source
1006
# is read locked and the target branches write locked. The local
1007
# branch is the low-latency branch.
1008
self['post_pull'] = []
1009
# invoked before a commit operation takes place.
1010
# the api signature is
1011
# (local, master, old_revno, old_revid, future_revno, future_revid,
1012
# tree_delta, future_tree).
1013
# old_revid is NULL_REVISION for the first commit to a branch
1014
# tree_delta is a TreeDelta object describing changes from the basis
1015
# revision, hooks MUST NOT modify this delta
1016
# future_tree is an in-memory tree obtained from
1017
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1018
self['pre_commit'] = []
1019
# invoked after a commit operation completes.
1020
# the api signature is
1021
# (local, master, old_revno, old_revid, new_revno, new_revid)
1022
# old_revid is NULL_REVISION for the first commit to a branch.
1023
self['post_commit'] = []
1024
# invoked after a uncommit operation completes.
1025
# the api signature is
1026
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1027
# local is the local branch or None, master is the target branch,
1028
# and an empty branch recieves new_revno of 0, new_revid of None.
1029
self['post_uncommit'] = []
1031
# Invoked after the tip of a branch changes.
1032
# the api signature is
1033
# (params) where params is a ChangeBranchTipParams with the members
1034
# (branch, old_revno, new_revno, old_revid, new_revid)
1035
self['post_change_branch_tip'] = []
1581
self.create_hook(HookPoint('set_rh',
1582
"Invoked whenever the revision history has been set via "
1583
"set_revision_history. The api signature is (branch, "
1584
"revision_history), and the branch will be write-locked. "
1585
"The set_rh hook can be expensive for bzr to trigger, a better "
1586
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1587
self.create_hook(HookPoint('open',
1588
"Called with the Branch object that has been opened after a "
1589
"branch is opened.", (1, 8), None))
1590
self.create_hook(HookPoint('post_push',
1591
"Called after a push operation completes. post_push is called "
1592
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1593
"bzr client.", (0, 15), None))
1594
self.create_hook(HookPoint('post_pull',
1595
"Called after a pull operation completes. post_pull is called "
1596
"with a bzrlib.branch.PullResult object and only runs in the "
1597
"bzr client.", (0, 15), None))
1598
self.create_hook(HookPoint('pre_commit',
1599
"Called after a commit is calculated but before it is is "
1600
"completed. pre_commit is called with (local, master, old_revno, "
1601
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1602
"). old_revid is NULL_REVISION for the first commit to a branch, "
1603
"tree_delta is a TreeDelta object describing changes from the "
1604
"basis revision. hooks MUST NOT modify this delta. "
1605
" future_tree is an in-memory tree obtained from "
1606
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1607
"tree.", (0,91), None))
1608
self.create_hook(HookPoint('post_commit',
1609
"Called in the bzr client after a commit has completed. "
1610
"post_commit is called with (local, master, old_revno, old_revid, "
1611
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1612
"commit to a branch.", (0, 15), None))
1613
self.create_hook(HookPoint('post_uncommit',
1614
"Called in the bzr client after an uncommit completes. "
1615
"post_uncommit is called with (local, master, old_revno, "
1616
"old_revid, new_revno, new_revid) where local is the local branch "
1617
"or None, master is the target branch, and an empty branch "
1618
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1619
self.create_hook(HookPoint('pre_change_branch_tip',
1620
"Called in bzr client and server before a change to the tip of a "
1621
"branch is made. pre_change_branch_tip is called with a "
1622
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1623
"commit, uncommit will all trigger this hook.", (1, 6), None))
1624
self.create_hook(HookPoint('post_change_branch_tip',
1625
"Called in bzr client and server after a change to the tip of a "
1626
"branch is made. post_change_branch_tip is called with a "
1627
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1628
"commit, uncommit will all trigger this hook.", (1, 4), None))
1629
self.create_hook(HookPoint('transform_fallback_location',
1630
"Called when a stacked branch is activating its fallback "
1631
"locations. transform_fallback_location is called with (branch, "
1632
"url), and should return a new url. Returning the same url "
1633
"allows it to be used as-is, returning a different one can be "
1634
"used to cause the branch to stack on a closer copy of that "
1635
"fallback_location. Note that the branch cannot have history "
1636
"accessing methods called on it during this hook because the "
1637
"fallback locations have not been activated. When there are "
1638
"multiple hooks installed for transform_fallback_location, "
1639
"all are called with the url returned from the previous hook."
1640
"The order is however undefined.", (1, 9), None))
1038
1643
# install the default hooks into the Branch class.
1194
1832
return self._initialize_helper(a_bzrdir, utf8_files)
1196
def open(self, a_bzrdir, _found=False):
1197
"""Return the branch object for a_bzrdir
1199
_found is a private parameter, do not use it. It is used to indicate
1200
if format probing has already be done.
1203
format = BranchFormat.find_format(a_bzrdir)
1204
if format.__class__ != self.__class__:
1205
raise AssertionError("wrong format %r found for %r" %
1207
transport = a_bzrdir.get_branch_transport(None)
1208
control_files = lockable_files.LockableFiles(transport, 'lock',
1210
return BzrBranch6(_format=self,
1211
_control_files=control_files,
1213
_repository=a_bzrdir.find_repository())
1215
def supports_tags(self):
1834
def make_tags(self, branch):
1835
"""See bzrlib.branch.BranchFormat.make_tags()."""
1836
return BasicTags(branch)
1838
def supports_set_append_revisions_only(self):
1842
class BzrBranchFormat8(BranchFormatMetadir):
1843
"""Metadir format supporting storing locations of subtree branches."""
1845
def _branch_class(self):
1848
def get_format_string(self):
1849
"""See BranchFormat.get_format_string()."""
1850
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
1852
def get_format_description(self):
1853
"""See BranchFormat.get_format_description()."""
1854
return "Branch format 8"
1856
def initialize(self, a_bzrdir):
1857
"""Create a branch of this format in a_bzrdir."""
1858
utf8_files = [('last-revision', '0 null:\n'),
1859
('branch.conf', ''),
1863
return self._initialize_helper(a_bzrdir, utf8_files)
1866
super(BzrBranchFormat8, self).__init__()
1867
self._matchingbzrdir.repository_format = \
1868
RepositoryFormatKnitPack5RichRoot()
1870
def make_tags(self, branch):
1871
"""See bzrlib.branch.BranchFormat.make_tags()."""
1872
return BasicTags(branch)
1874
def supports_set_append_revisions_only(self):
1877
def supports_stacking(self):
1880
supports_reference_locations = True
1883
class BzrBranchFormat7(BzrBranchFormat8):
1884
"""Branch format with last-revision, tags, and a stacked location pointer.
1886
The stacked location pointer is passed down to the repository and requires
1887
a repository format with supports_external_lookups = True.
1889
This format was introduced in bzr 1.6.
1892
def initialize(self, a_bzrdir):
1893
"""Create a branch of this format in a_bzrdir."""
1894
utf8_files = [('last-revision', '0 null:\n'),
1895
('branch.conf', ''),
1898
return self._initialize_helper(a_bzrdir, utf8_files)
1900
def _branch_class(self):
1903
def get_format_string(self):
1904
"""See BranchFormat.get_format_string()."""
1905
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1907
def get_format_description(self):
1908
"""See BranchFormat.get_format_description()."""
1909
return "Branch format 7"
1911
def supports_set_append_revisions_only(self):
1914
supports_reference_locations = False
1219
1917
class BranchReferenceFormat(BranchFormat):
1406
2151
This performs the actual writing to disk.
1407
2152
It is intended to be called by BzrBranch5.set_revision_history."""
1408
self.control_files.put_bytes(
1409
'revision-history', '\n'.join(history))
2153
self._transport.put_bytes(
2154
'revision-history', '\n'.join(history),
2155
mode=self.bzrdir._get_file_mode())
1411
2157
@needs_write_lock
1412
2158
def set_revision_history(self, rev_history):
1413
2159
"""See Branch.set_revision_history."""
1414
2160
if 'evil' in debug.debug_flags:
1415
2161
mutter_callsite(3, "set_revision_history scales with history.")
2162
check_not_reserved_id = _mod_revision.check_not_reserved_id
2163
for rev_id in rev_history:
2164
check_not_reserved_id(rev_id)
2165
if Branch.hooks['post_change_branch_tip']:
2166
# Don't calculate the last_revision_info() if there are no hooks
2168
old_revno, old_revid = self.last_revision_info()
2169
if len(rev_history) == 0:
2170
revid = _mod_revision.NULL_REVISION
2172
revid = rev_history[-1]
2173
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1416
2174
self._write_revision_history(rev_history)
1417
2175
self._clear_cached_state()
1418
2176
self._cache_revision_history(rev_history)
1419
2177
for hook in Branch.hooks['set_rh']:
1420
2178
hook(self, rev_history)
1422
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1423
"""Run the post_change_branch_tip hooks."""
1424
hooks = Branch.hooks['post_change_branch_tip']
2179
if Branch.hooks['post_change_branch_tip']:
2180
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2182
def _synchronize_history(self, destination, revision_id):
2183
"""Synchronize last revision and revision history between branches.
2185
This version is most efficient when the destination is also a
2186
BzrBranch5, but works for BzrBranch6 as long as the revision
2187
history is the true lefthand parent history, and all of the revisions
2188
are in the destination's repository. If not, set_revision_history
2191
:param destination: The branch to copy the history into
2192
:param revision_id: The revision-id to truncate history at. May
2193
be None to copy complete history.
2195
if not isinstance(destination._format, BzrBranchFormat5):
2196
super(BzrBranch, self)._synchronize_history(
2197
destination, revision_id)
1427
new_revno, new_revid = self.last_revision_info()
1428
params = ChangeBranchTipParams(
1429
self, old_revno, new_revno, old_revid, new_revid)
2199
if revision_id == _mod_revision.NULL_REVISION:
2202
new_history = self.revision_history()
2203
if revision_id is not None and new_history != []:
2205
new_history = new_history[:new_history.index(revision_id) + 1]
2207
rev = self.repository.get_revision(revision_id)
2208
new_history = rev.get_history(self.repository)[1:]
2209
destination.set_revision_history(new_history)
1433
2211
@needs_write_lock
1434
2212
def set_last_revision_info(self, revno, revision_id):
1435
2213
"""Set the last revision of this branch.
1499
2250
self.set_revision_history(self._lefthand_history(revision_id,
1500
2251
last_rev, other_branch))
1503
def update_revisions(self, other, stop_revision=None, overwrite=False):
1504
"""See Branch.update_revisions."""
1507
other_last_revno, other_last_revision = other.last_revision_info()
1508
if stop_revision is None:
1509
stop_revision = other_last_revision
1510
if _mod_revision.is_null(stop_revision):
1511
# if there are no commits, we're done.
1513
# whats the current last revision, before we fetch [and change it
1515
last_rev = _mod_revision.ensure_null(self.last_revision())
1516
# we fetch here so that we don't process data twice in the common
1517
# case of having something to pull, and so that the check for
1518
# already merged can operate on the just fetched graph, which will
1519
# be cached in memory.
1520
self.fetch(other, stop_revision)
1521
# Check to see if one is an ancestor of the other
1523
heads = self.repository.get_graph().heads([stop_revision,
1525
if heads == set([last_rev]):
1526
# The current revision is a decendent of the target,
1529
elif heads == set([stop_revision, last_rev]):
1530
# These branches have diverged
1531
raise errors.DivergedBranches(self, other)
1532
elif heads != set([stop_revision]):
1533
raise AssertionError("invalid heads: %r" % heads)
1534
if other_last_revision == stop_revision:
1535
self.set_last_revision_info(other_last_revno,
1536
other_last_revision)
1538
# TODO: jam 2007-11-29 Is there a way to determine the
1539
# revno without searching all of history??
1541
self.generate_revision_history(stop_revision)
1543
self.generate_revision_history(stop_revision,
1544
last_rev=last_rev, other_branch=other)
1548
2253
def basis_tree(self):
1549
2254
"""See Branch.basis_tree."""
1550
2255
return self.repository.revision_tree(self.last_revision())
1553
def pull(self, source, overwrite=False, stop_revision=None,
1554
_hook_master=None, run_hooks=True, possible_transports=None):
1557
:param _hook_master: Private parameter - set the branch to
1558
be supplied as the master to push hooks.
1559
:param run_hooks: Private parameter - if false, this branch
1560
is being called because it's the master of the primary branch,
1561
so it should not run its hooks.
1563
result = PullResult()
1564
result.source_branch = source
1565
result.target_branch = self
1568
result.old_revno, result.old_revid = self.last_revision_info()
1569
self.update_revisions(source, stop_revision, overwrite=overwrite)
1570
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1571
result.new_revno, result.new_revid = self.last_revision_info()
1573
result.master_branch = _hook_master
1574
result.local_branch = self
1576
result.master_branch = self
1577
result.local_branch = None
1579
for hook in Branch.hooks['post_pull']:
1585
2257
def _get_parent_location(self):
1586
2258
_locs = ['parent', 'pull', 'x-pull']
1587
2259
for l in _locs:
1589
return self.control_files.get(l).read().strip('\n')
2261
return self._transport.get_bytes(l).strip('\n')
1590
2262
except errors.NoSuchFile:
1595
def push(self, target, overwrite=False, stop_revision=None,
1596
_override_hook_source_branch=None):
1599
This is the basic concrete implementation of push()
1601
:param _override_hook_source_branch: If specified, run
1602
the hooks passing this Branch as the source, rather than self.
1603
This is for use of RemoteBranch, where push is delegated to the
1604
underlying vfs-based Branch.
1606
# TODO: Public option to disable running hooks - should be trivial but
1610
result = self._push_with_bound_branches(target, overwrite,
1612
_override_hook_source_branch=_override_hook_source_branch)
1617
def _push_with_bound_branches(self, target, overwrite,
1619
_override_hook_source_branch=None):
1620
"""Push from self into target, and into target's master if any.
1622
This is on the base BzrBranch class even though it doesn't support
1623
bound branches because the *target* might be bound.
1626
if _override_hook_source_branch:
1627
result.source_branch = _override_hook_source_branch
1628
for hook in Branch.hooks['post_push']:
1631
bound_location = target.get_bound_location()
1632
if bound_location and target.base != bound_location:
1633
# there is a master branch.
1635
# XXX: Why the second check? Is it even supported for a branch to
1636
# be bound to itself? -- mbp 20070507
1637
master_branch = target.get_master_branch()
1638
master_branch.lock_write()
1640
# push into the master from this branch.
1641
self._basic_push(master_branch, overwrite, stop_revision)
1642
# and push into the target branch from this. Note that we push from
1643
# this branch again, because its considered the highest bandwidth
1645
result = self._basic_push(target, overwrite, stop_revision)
1646
result.master_branch = master_branch
1647
result.local_branch = target
1651
master_branch.unlock()
1654
result = self._basic_push(target, overwrite, stop_revision)
1655
# TODO: Why set master_branch and local_branch if there's no
1656
# binding? Maybe cleaner to just leave them unset? -- mbp
1658
result.master_branch = target
1659
result.local_branch = None
1663
2266
def _basic_push(self, target, overwrite, stop_revision):
1664
2267
"""Basic implementation of push without bound branches or hooks.
1666
Must be called with self read locked and target write locked.
2269
Must be called with source read locked and target write locked.
1668
result = PushResult()
2271
result = BranchPushResult()
1669
2272
result.source_branch = self
1670
2273
result.target_branch = target
1671
#import pdb; pdb.set_trace()
1674
result.old_revno, result.old_revid = target.last_revision_info()
1676
target.update_revisions(self, stop_revision)
1677
except errors.DivergedBranches:
1681
target.set_revision_history(self.revision_history())
1682
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1683
result.new_revno, result.new_revid = target.last_revision_info()
2274
result.old_revno, result.old_revid = target.last_revision_info()
2275
self.update_references(target)
2276
if result.old_revid != self.last_revision():
2277
# We assume that during 'push' this repository is closer than
2279
graph = self.repository.get_graph(target.repository)
2280
target.update_revisions(self, stop_revision,
2281
overwrite=overwrite, graph=graph)
2282
if self._push_should_merge_tags():
2283
result.tag_conflicts = self.tags.merge_to(target.tags,
2285
result.new_revno, result.new_revid = target.last_revision_info()
1688
def get_parent(self):
1689
"""See Branch.get_parent."""
1690
parent = self._get_parent_location()
1693
# This is an old-format absolute path to a local branch
1694
# turn it into a url
1695
if parent.startswith('/'):
1696
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1698
return urlutils.join(self.base[:-1], parent)
1699
except errors.InvalidURLJoin, e:
1700
raise errors.InaccessibleParent(parent, self.base)
2288
def get_stacked_on_url(self):
2289
raise errors.UnstackableBranchFormat(self._format, self.base)
1702
2291
def set_push_location(self, location):
1703
2292
"""See Branch.set_push_location."""
1985
2517
"""Set the parent branch"""
1986
2518
return self._get_config_location('parent_location')
2521
def _set_all_reference_info(self, info_dict):
2522
"""Replace all reference info stored in a branch.
2524
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2527
writer = rio.RioWriter(s)
2528
for key, (tree_path, branch_location) in info_dict.iteritems():
2529
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2530
branch_location=branch_location)
2531
writer.write_stanza(stanza)
2532
self._transport.put_bytes('references', s.getvalue())
2533
self._reference_info = info_dict
2536
def _get_all_reference_info(self):
2537
"""Return all the reference info stored in a branch.
2539
:return: A dict of {file_id: (tree_path, branch_location)}
2541
if self._reference_info is not None:
2542
return self._reference_info
2543
rio_file = self._transport.get('references')
2545
stanzas = rio.read_stanzas(rio_file)
2546
info_dict = dict((s['file_id'], (s['tree_path'],
2547
s['branch_location'])) for s in stanzas)
2550
self._reference_info = info_dict
2553
def set_reference_info(self, file_id, tree_path, branch_location):
2554
"""Set the branch location to use for a tree reference.
2556
:param file_id: The file-id of the tree reference.
2557
:param tree_path: The path of the tree reference in the tree.
2558
:param branch_location: The location of the branch to retrieve tree
2561
info_dict = self._get_all_reference_info()
2562
info_dict[file_id] = (tree_path, branch_location)
2563
if None in (tree_path, branch_location):
2564
if tree_path is not None:
2565
raise ValueError('tree_path must be None when branch_location'
2567
if branch_location is not None:
2568
raise ValueError('branch_location must be None when tree_path'
2570
del info_dict[file_id]
2571
self._set_all_reference_info(info_dict)
2573
def get_reference_info(self, file_id):
2574
"""Get the tree_path and branch_location for a tree reference.
2576
:return: a tuple of (tree_path, branch_location)
2578
return self._get_all_reference_info().get(file_id, (None, None))
2580
def reference_parent(self, file_id, path, possible_transports=None):
2581
"""Return the parent branch for a tree-reference file_id.
2583
:param file_id: The file_id of the tree reference
2584
:param path: The path of the file_id in the tree
2585
:return: A branch associated with the file_id
2587
branch_location = self.get_reference_info(file_id)[1]
2588
if branch_location is None:
2589
return Branch.reference_parent(self, file_id, path,
2590
possible_transports)
2591
branch_location = urlutils.join(self.base, branch_location)
2592
return Branch.open(branch_location,
2593
possible_transports=possible_transports)
1988
2595
def set_push_location(self, location):
1989
2596
"""See Branch.set_push_location."""
1990
2597
self._set_config_location('push_location', location)
2213
2827
new_branch.tags._set_tag_dict({})
2215
2829
# Copying done; now update target format
2216
new_branch.control_files.put_utf8('format',
2217
format.get_format_string())
2830
new_branch._transport.put_bytes('format',
2831
format.get_format_string(),
2832
mode=new_branch.bzrdir._get_file_mode())
2219
2834
# Clean up old files
2220
new_branch.control_files._transport.delete('revision-history')
2835
new_branch._transport.delete('revision-history')
2222
2837
branch.set_parent(None)
2223
2838
except errors.NoSuchFile:
2225
2840
branch.set_bound_location(None)
2843
class Converter6to7(object):
2844
"""Perform an in-place upgrade of format 6 to format 7"""
2846
def convert(self, branch):
2847
format = BzrBranchFormat7()
2848
branch._set_config_location('stacked_on_location', '')
2849
# update target format
2850
branch._transport.put_bytes('format', format.get_format_string())
2853
class Converter7to8(object):
2854
"""Perform an in-place upgrade of format 6 to format 7"""
2856
def convert(self, branch):
2857
format = BzrBranchFormat8()
2858
branch._transport.put_bytes('references', '')
2859
# update target format
2860
branch._transport.put_bytes('format', format.get_format_string())
2863
def _run_with_write_locked_target(target, callable, *args, **kwargs):
2864
"""Run ``callable(*args, **kwargs)``, write-locking target for the
2867
_run_with_write_locked_target will attempt to release the lock it acquires.
2869
If an exception is raised by callable, then that exception *will* be
2870
propagated, even if the unlock attempt raises its own error. Thus
2871
_run_with_write_locked_target should be preferred to simply doing::
2875
return callable(*args, **kwargs)
2880
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
2881
# should share code?
2884
result = callable(*args, **kwargs)
2886
exc_info = sys.exc_info()
2890
raise exc_info[0], exc_info[1], exc_info[2]
2896
class InterBranch(InterObject):
2897
"""This class represents operations taking place between two branches.
2899
Its instances have methods like pull() and push() and contain
2900
references to the source and target repositories these operations
2901
can be carried out on.
2905
"""The available optimised InterBranch types."""
2908
def _get_branch_formats_to_test():
2909
"""Return a tuple with the Branch formats to use when testing."""
2910
raise NotImplementedError(self._get_branch_formats_to_test)
2912
def pull(self, overwrite=False, stop_revision=None,
2913
possible_transports=None, local=False):
2914
"""Mirror source into target branch.
2916
The target branch is considered to be 'local', having low latency.
2918
:returns: PullResult instance
2920
raise NotImplementedError(self.pull)
2922
def update_revisions(self, stop_revision=None, overwrite=False,
2924
"""Pull in new perfect-fit revisions.
2926
:param stop_revision: Updated until the given revision
2927
:param overwrite: Always set the branch pointer, rather than checking
2928
to see if it is a proper descendant.
2929
:param graph: A Graph object that can be used to query history
2930
information. This can be None.
2933
raise NotImplementedError(self.update_revisions)
2935
def push(self, overwrite=False, stop_revision=None,
2936
_override_hook_source_branch=None):
2937
"""Mirror the source branch into the target branch.
2939
The source branch is considered to be 'local', having low latency.
2941
raise NotImplementedError(self.push)
2944
class GenericInterBranch(InterBranch):
2945
"""InterBranch implementation that uses public Branch functions.
2949
def _get_branch_formats_to_test():
2950
return BranchFormat._default_format, BranchFormat._default_format
2952
def update_revisions(self, stop_revision=None, overwrite=False,
2954
"""See InterBranch.update_revisions()."""
2955
self.source.lock_read()
2957
other_revno, other_last_revision = self.source.last_revision_info()
2958
stop_revno = None # unknown
2959
if stop_revision is None:
2960
stop_revision = other_last_revision
2961
if _mod_revision.is_null(stop_revision):
2962
# if there are no commits, we're done.
2964
stop_revno = other_revno
2966
# what's the current last revision, before we fetch [and change it
2968
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2969
# we fetch here so that we don't process data twice in the common
2970
# case of having something to pull, and so that the check for
2971
# already merged can operate on the just fetched graph, which will
2972
# be cached in memory.
2973
self.target.fetch(self.source, stop_revision)
2974
# Check to see if one is an ancestor of the other
2977
graph = self.target.repository.get_graph()
2978
if self.target._check_if_descendant_or_diverged(
2979
stop_revision, last_rev, graph, self.source):
2980
# stop_revision is a descendant of last_rev, but we aren't
2981
# overwriting, so we're done.
2983
if stop_revno is None:
2985
graph = self.target.repository.get_graph()
2986
this_revno, this_last_revision = \
2987
self.target.last_revision_info()
2988
stop_revno = graph.find_distance_to_null(stop_revision,
2989
[(other_last_revision, other_revno),
2990
(this_last_revision, this_revno)])
2991
self.target.set_last_revision_info(stop_revno, stop_revision)
2993
self.source.unlock()
2995
def pull(self, overwrite=False, stop_revision=None,
2996
possible_transports=None, _hook_master=None, run_hooks=True,
2997
_override_hook_target=None, local=False):
3000
:param _hook_master: Private parameter - set the branch to
3001
be supplied as the master to pull hooks.
3002
:param run_hooks: Private parameter - if false, this branch
3003
is being called because it's the master of the primary branch,
3004
so it should not run its hooks.
3005
:param _override_hook_target: Private parameter - set the branch to be
3006
supplied as the target_branch to pull hooks.
3007
:param local: Only update the local branch, and not the bound branch.
3009
# This type of branch can't be bound.
3011
raise errors.LocalRequiresBoundBranch()
3012
result = PullResult()
3013
result.source_branch = self.source
3014
if _override_hook_target is None:
3015
result.target_branch = self.target
3017
result.target_branch = _override_hook_target
3018
self.source.lock_read()
3020
# We assume that during 'pull' the target repository is closer than
3022
self.source.update_references(self.target)
3023
graph = self.target.repository.get_graph(self.source.repository)
3024
# TODO: Branch formats should have a flag that indicates
3025
# that revno's are expensive, and pull() should honor that flag.
3027
result.old_revno, result.old_revid = \
3028
self.target.last_revision_info()
3029
self.target.update_revisions(self.source, stop_revision,
3030
overwrite=overwrite, graph=graph)
3031
# TODO: The old revid should be specified when merging tags,
3032
# so a tags implementation that versions tags can only
3033
# pull in the most recent changes. -- JRV20090506
3034
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3036
result.new_revno, result.new_revid = self.target.last_revision_info()
3038
result.master_branch = _hook_master
3039
result.local_branch = result.target_branch
3041
result.master_branch = result.target_branch
3042
result.local_branch = None
3044
for hook in Branch.hooks['post_pull']:
3047
self.source.unlock()
3050
def push(self, overwrite=False, stop_revision=None,
3051
_override_hook_source_branch=None):
3052
"""See InterBranch.push.
3054
This is the basic concrete implementation of push()
3056
:param _override_hook_source_branch: If specified, run
3057
the hooks passing this Branch as the source, rather than self.
3058
This is for use of RemoteBranch, where push is delegated to the
3059
underlying vfs-based Branch.
3061
# TODO: Public option to disable running hooks - should be trivial but
3063
self.source.lock_read()
3065
return _run_with_write_locked_target(
3066
self.target, self._push_with_bound_branches, overwrite,
3068
_override_hook_source_branch=_override_hook_source_branch)
3070
self.source.unlock()
3073
def _push_with_bound_branches(self, overwrite, stop_revision,
3074
_override_hook_source_branch=None):
3075
"""Push from source into target, and into target's master if any.
3078
if _override_hook_source_branch:
3079
result.source_branch = _override_hook_source_branch
3080
for hook in Branch.hooks['post_push']:
3083
bound_location = self.target.get_bound_location()
3084
if bound_location and self.target.base != bound_location:
3085
# there is a master branch.
3087
# XXX: Why the second check? Is it even supported for a branch to
3088
# be bound to itself? -- mbp 20070507
3089
master_branch = self.target.get_master_branch()
3090
master_branch.lock_write()
3092
# push into the master from the source branch.
3093
self.source._basic_push(master_branch, overwrite, stop_revision)
3094
# and push into the target branch from the source. Note that we
3095
# push from the source branch again, because its considered the
3096
# highest bandwidth repository.
3097
result = self.source._basic_push(self.target, overwrite,
3099
result.master_branch = master_branch
3100
result.local_branch = self.target
3104
master_branch.unlock()
3107
result = self.source._basic_push(self.target, overwrite,
3109
# TODO: Why set master_branch and local_branch if there's no
3110
# binding? Maybe cleaner to just leave them unset? -- mbp
3112
result.master_branch = self.target
3113
result.local_branch = None
3118
def is_compatible(self, source, target):
3119
# GenericBranch uses the public API, so always compatible
3123
class InterToBranch5(GenericInterBranch):
3126
def _get_branch_formats_to_test():
3127
return BranchFormat._default_format, BzrBranchFormat5()
3129
def pull(self, overwrite=False, stop_revision=None,
3130
possible_transports=None, run_hooks=True,
3131
_override_hook_target=None, local=False):
3132
"""Pull from source into self, updating my master if any.
3134
:param run_hooks: Private parameter - if false, this branch
3135
is being called because it's the master of the primary branch,
3136
so it should not run its hooks.
3138
bound_location = self.target.get_bound_location()
3139
if local and not bound_location:
3140
raise errors.LocalRequiresBoundBranch()
3141
master_branch = None
3142
if not local and bound_location and self.source.base != bound_location:
3143
# not pulling from master, so we need to update master.
3144
master_branch = self.target.get_master_branch(possible_transports)
3145
master_branch.lock_write()
3148
# pull from source into master.
3149
master_branch.pull(self.source, overwrite, stop_revision,
3151
return super(InterToBranch5, self).pull(overwrite,
3152
stop_revision, _hook_master=master_branch,
3153
run_hooks=run_hooks,
3154
_override_hook_target=_override_hook_target)
3157
master_branch.unlock()
3160
InterBranch.register_optimiser(GenericInterBranch)
3161
InterBranch.register_optimiser(InterToBranch5)