200
150
possible_transports)
201
151
return control.open_branch(), relpath
203
def _push_should_merge_tags(self):
204
"""Should _basic_push merge this branch's tags into the target?
206
The default implementation returns False if this branch has no tags,
207
and True the rest of the time. Subclasses may override this.
209
return self.supports_tags() and self.tags.get_tag_dict()
211
153
def get_config(self):
212
"""Get a bzrlib.config.BranchConfig for this Branch.
214
This can then be used to get and set configuration options for the
217
:return: A bzrlib.config.BranchConfig.
219
154
return BranchConfig(self)
221
def _get_config(self):
222
"""Get the concrete config for just the config in this branch.
224
This is not intended for client use; see Branch.get_config for the
229
:return: An object supporting get_option and set_option.
231
raise NotImplementedError(self._get_config)
233
def _get_fallback_repository(self, url):
234
"""Get the repository we fallback to at url."""
235
url = urlutils.join(self.base, url)
236
a_branch = Branch.open(url,
237
possible_transports=[self.bzrdir.root_transport])
238
return a_branch.repository
241
def _get_tags_bytes(self):
242
"""Get the bytes of a serialised tags dict.
244
Note that not all branches support tags, nor do all use the same tags
245
logic: this method is specific to BasicTags. Other tag implementations
246
may use the same method name and behave differently, safely, because
247
of the double-dispatch via
248
format.make_tags->tags_instance->get_tags_dict.
250
:return: The bytes of the tags file.
251
:seealso: Branch._set_tags_bytes.
253
if self._tags_bytes is None:
254
self._tags_bytes = self._transport.get_bytes('tags')
255
return self._tags_bytes
257
def _get_nick(self, local=False, possible_transports=None):
258
config = self.get_config()
259
# explicit overrides master, but don't look for master if local is True
260
if not local and not config.has_explicit_nickname():
262
master = self.get_master_branch(possible_transports)
263
if master and self.user_url == master.user_url:
264
raise errors.RecursiveBind(self.user_url)
265
if master is not None:
266
# return the master branch value
268
except errors.RecursiveBind, e:
270
except errors.BzrError, e:
271
# Silently fall back to local implicit nick if the master is
273
mutter("Could not connect to bound branch, "
274
"falling back to local nick.\n " + str(e))
275
return config.get_nickname()
157
return self.get_config().get_nickname()
277
159
def _set_nick(self, nick):
278
160
self.get_config().set_user_option('nickname', nick, warn_masked=True)
430
211
:return: A dictionary mapping revision_id => dotted revno.
213
last_revision = self.last_revision()
214
revision_graph = self.repository.get_revision_graph(last_revision)
215
merge_sorted_revisions = tsort.merge_sort(
432
220
revision_id_to_revno = dict((rev_id, revno)
433
for rev_id, depth, revno, end_of_merge
434
in self.iter_merge_sorted_revisions())
221
for seq_num, rev_id, depth, revno, end_of_merge
222
in merge_sorted_revisions)
435
223
return revision_id_to_revno
438
def iter_merge_sorted_revisions(self, start_revision_id=None,
439
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
440
"""Walk the revisions for a branch in merge sorted order.
442
Merge sorted order is the output from a merge-aware,
443
topological sort, i.e. all parents come before their
444
children going forward; the opposite for reverse.
446
:param start_revision_id: the revision_id to begin walking from.
447
If None, the branch tip is used.
448
:param stop_revision_id: the revision_id to terminate the walk
449
after. If None, the rest of history is included.
450
:param stop_rule: if stop_revision_id is not None, the precise rule
451
to use for termination:
452
* 'exclude' - leave the stop revision out of the result (default)
453
* 'include' - the stop revision is the last item in the result
454
* 'with-merges' - include the stop revision and all of its
455
merged revisions in the result
456
* 'with-merges-without-common-ancestry' - filter out revisions
457
that are in both ancestries
458
:param direction: either 'reverse' or 'forward':
459
* reverse means return the start_revision_id first, i.e.
460
start at the most recent revision and go backwards in history
461
* forward returns tuples in the opposite order to reverse.
462
Note in particular that forward does *not* do any intelligent
463
ordering w.r.t. depth as some clients of this API may like.
464
(If required, that ought to be done at higher layers.)
466
:return: an iterator over (revision_id, depth, revno, end_of_merge)
469
* revision_id: the unique id of the revision
470
* depth: How many levels of merging deep this node has been
472
* revno_sequence: This field provides a sequence of
473
revision numbers for all revisions. The format is:
474
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
475
branch that the revno is on. From left to right the REVNO numbers
476
are the sequence numbers within that branch of the revision.
477
* end_of_merge: When True the next node (earlier in history) is
478
part of a different merge.
480
# Note: depth and revno values are in the context of the branch so
481
# we need the full graph to get stable numbers, regardless of the
483
if self._merge_sorted_revisions_cache is None:
484
last_revision = self.last_revision()
485
known_graph = self.repository.get_known_graph_ancestry(
487
self._merge_sorted_revisions_cache = known_graph.merge_sort(
489
filtered = self._filter_merge_sorted_revisions(
490
self._merge_sorted_revisions_cache, start_revision_id,
491
stop_revision_id, stop_rule)
492
# Make sure we don't return revisions that are not part of the
493
# start_revision_id ancestry.
494
filtered = self._filter_start_non_ancestors(filtered)
495
if direction == 'reverse':
497
if direction == 'forward':
498
return reversed(list(filtered))
500
raise ValueError('invalid direction %r' % direction)
502
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
503
start_revision_id, stop_revision_id, stop_rule):
504
"""Iterate over an inclusive range of sorted revisions."""
505
rev_iter = iter(merge_sorted_revisions)
506
if start_revision_id is not None:
507
for node in rev_iter:
508
rev_id = node.key[-1]
509
if rev_id != start_revision_id:
512
# The decision to include the start or not
513
# depends on the stop_rule if a stop is provided
514
# so pop this node back into the iterator
515
rev_iter = chain(iter([node]), rev_iter)
517
if stop_revision_id is None:
519
for node in rev_iter:
520
rev_id = node.key[-1]
521
yield (rev_id, node.merge_depth, node.revno,
523
elif stop_rule == 'exclude':
524
for node in rev_iter:
525
rev_id = node.key[-1]
526
if rev_id == stop_revision_id:
528
yield (rev_id, node.merge_depth, node.revno,
530
elif stop_rule == 'include':
531
for node in rev_iter:
532
rev_id = node.key[-1]
533
yield (rev_id, node.merge_depth, node.revno,
535
if rev_id == stop_revision_id:
537
elif stop_rule == 'with-merges-without-common-ancestry':
538
# We want to exclude all revisions that are already part of the
539
# stop_revision_id ancestry.
540
graph = self.repository.get_graph()
541
ancestors = graph.find_unique_ancestors(start_revision_id,
543
for node in rev_iter:
544
rev_id = node.key[-1]
545
if rev_id not in ancestors:
547
yield (rev_id, node.merge_depth, node.revno,
549
elif stop_rule == 'with-merges':
550
stop_rev = self.repository.get_revision(stop_revision_id)
551
if stop_rev.parent_ids:
552
left_parent = stop_rev.parent_ids[0]
554
left_parent = _mod_revision.NULL_REVISION
555
# left_parent is the actual revision we want to stop logging at,
556
# since we want to show the merged revisions after the stop_rev too
557
reached_stop_revision_id = False
558
revision_id_whitelist = []
559
for node in rev_iter:
560
rev_id = node.key[-1]
561
if rev_id == left_parent:
562
# reached the left parent after the stop_revision
564
if (not reached_stop_revision_id or
565
rev_id in revision_id_whitelist):
566
yield (rev_id, node.merge_depth, node.revno,
568
if reached_stop_revision_id or rev_id == stop_revision_id:
569
# only do the merged revs of rev_id from now on
570
rev = self.repository.get_revision(rev_id)
572
reached_stop_revision_id = True
573
revision_id_whitelist.extend(rev.parent_ids)
575
raise ValueError('invalid stop_rule %r' % stop_rule)
577
def _filter_start_non_ancestors(self, rev_iter):
578
# If we started from a dotted revno, we want to consider it as a tip
579
# and don't want to yield revisions that are not part of its
580
# ancestry. Given the order guaranteed by the merge sort, we will see
581
# uninteresting descendants of the first parent of our tip before the
583
first = rev_iter.next()
584
(rev_id, merge_depth, revno, end_of_merge) = first
587
# We start at a mainline revision so by definition, all others
588
# revisions in rev_iter are ancestors
589
for node in rev_iter:
594
pmap = self.repository.get_parent_map([rev_id])
595
parents = pmap.get(rev_id, [])
597
whitelist.update(parents)
599
# If there is no parents, there is nothing of interest left
601
# FIXME: It's hard to test this scenario here as this code is never
602
# called in that case. -- vila 20100322
605
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
607
if rev_id in whitelist:
608
pmap = self.repository.get_parent_map([rev_id])
609
parents = pmap.get(rev_id, [])
610
whitelist.remove(rev_id)
611
whitelist.update(parents)
613
# We've reached the mainline, there is nothing left to
617
# A revision that is not part of the ancestry of our
620
yield (rev_id, merge_depth, revno, end_of_merge)
622
225
def leave_lock_in_place(self):
623
226
"""Tell this branch object not to release the physical lock when this
624
227
object is unlocked.
626
229
If lock_write doesn't return a token, then this method is not supported.
628
231
self.control_files.leave_in_place()
636
239
self.control_files.dont_leave_in_place()
241
def abspath(self, name):
242
"""Return absolute filename for something in the branch
244
XXX: Robert Collins 20051017 what is this used for? why is it a branch
245
method and not a tree method.
247
raise NotImplementedError(self.abspath)
638
249
def bind(self, other):
639
250
"""Bind the local branch the other branch.
641
252
:param other: The branch to bind to
642
253
:type other: Branch
644
raise errors.UpgradeRequired(self.user_url)
646
def set_append_revisions_only(self, enabled):
647
if not self._format.supports_set_append_revisions_only():
648
raise errors.UpgradeRequired(self.user_url)
653
self.get_config().set_user_option('append_revisions_only', value,
656
def set_reference_info(self, file_id, tree_path, branch_location):
657
"""Set the branch location to use for a tree reference."""
658
raise errors.UnsupportedOperation(self.set_reference_info, self)
660
def get_reference_info(self, file_id):
661
"""Get the tree_path and branch_location for a tree reference."""
662
raise errors.UnsupportedOperation(self.get_reference_info, self)
255
raise errors.UpgradeRequired(self.base)
664
257
@needs_write_lock
665
def fetch(self, from_branch, last_revision=None, pb=None, fetch_spec=None):
258
def fetch(self, from_branch, last_revision=None, pb=None):
666
259
"""Copy revisions from from_branch into this branch.
668
261
:param from_branch: Where to copy from.
669
262
:param last_revision: What revision to stop at (None for at the end
671
264
:param pb: An optional progress bar to use.
672
:param fetch_spec: If specified, a SearchResult or
673
PendingAncestryResult that describes which revisions to copy. This
674
allows copying multiple heads at once. Mutually exclusive with
266
Returns the copied revision count and the failed revisions in a tuple:
678
if fetch_spec is not None and last_revision is not None:
679
raise AssertionError(
680
"fetch_spec and last_revision are mutually exclusive.")
681
269
if self.base == from_branch.base:
684
symbol_versioning.warn(
685
symbol_versioning.deprecated_in((1, 14, 0))
686
% "pb parameter to fetch()")
272
nested_pb = ui.ui_factory.nested_progress_bar()
687
277
from_branch.lock_read()
689
if last_revision is None and fetch_spec is None:
279
if last_revision is None:
280
pb.update('get source history')
690
281
last_revision = from_branch.last_revision()
691
last_revision = _mod_revision.ensure_null(last_revision)
282
if last_revision is None:
283
last_revision = _mod_revision.NULL_REVISION
692
284
return self.repository.fetch(from_branch.repository,
693
285
revision_id=last_revision,
694
pb=pb, fetch_spec=fetch_spec)
288
if nested_pb is not None:
696
290
from_branch.unlock()
698
292
def get_bound_location(self):
762
357
def set_revision_history(self, rev_history):
763
358
raise NotImplementedError(self.set_revision_history)
766
def set_parent(self, url):
767
"""See Branch.set_parent."""
768
# TODO: Maybe delete old location files?
769
# URLs should never be unicode, even on the local fs,
770
# FIXUP this and get_parent in a future branch format bump:
771
# read and rewrite the file. RBC 20060125
773
if isinstance(url, unicode):
775
url = url.encode('ascii')
776
except UnicodeEncodeError:
777
raise errors.InvalidURL(url,
778
"Urls must be 7-bit ascii, "
779
"use bzrlib.urlutils.escape")
780
url = urlutils.relative_url(self.base, url)
781
self._set_parent_location(url)
784
def set_stacked_on_url(self, url):
785
"""Set the URL this branch is stacked against.
787
:raises UnstackableBranchFormat: If the branch does not support
789
:raises UnstackableRepositoryFormat: If the repository does not support
792
if not self._format.supports_stacking():
793
raise errors.UnstackableBranchFormat(self._format, self.user_url)
794
# XXX: Changing from one fallback repository to another does not check
795
# that all the data you need is present in the new fallback.
796
# Possibly it should.
797
self._check_stackable_repo()
800
old_url = self.get_stacked_on_url()
801
except (errors.NotStacked, errors.UnstackableBranchFormat,
802
errors.UnstackableRepositoryFormat):
806
self._activate_fallback_location(url)
807
# write this out after the repository is stacked to avoid setting a
808
# stacked config that doesn't work.
809
self._set_config_location('stacked_on_location', url)
812
"""Change a branch to be unstacked, copying data as needed.
814
Don't call this directly, use set_stacked_on_url(None).
816
pb = ui.ui_factory.nested_progress_bar()
818
pb.update("Unstacking")
819
# The basic approach here is to fetch the tip of the branch,
820
# including all available ghosts, from the existing stacked
821
# repository into a new repository object without the fallbacks.
823
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
824
# correct for CHKMap repostiories
825
old_repository = self.repository
826
if len(old_repository._fallback_repositories) != 1:
827
raise AssertionError("can't cope with fallback repositories "
828
"of %r (fallbacks: %r)" % (old_repository,
829
old_repository._fallback_repositories))
830
# Open the new repository object.
831
# Repositories don't offer an interface to remove fallback
832
# repositories today; take the conceptually simpler option and just
833
# reopen it. We reopen it starting from the URL so that we
834
# get a separate connection for RemoteRepositories and can
835
# stream from one of them to the other. This does mean doing
836
# separate SSH connection setup, but unstacking is not a
837
# common operation so it's tolerable.
838
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
839
new_repository = new_bzrdir.find_repository()
840
if new_repository._fallback_repositories:
841
raise AssertionError("didn't expect %r to have "
842
"fallback_repositories"
843
% (self.repository,))
844
# Replace self.repository with the new repository.
845
# Do our best to transfer the lock state (i.e. lock-tokens and
846
# lock count) of self.repository to the new repository.
847
lock_token = old_repository.lock_write().repository_token
848
self.repository = new_repository
849
if isinstance(self, remote.RemoteBranch):
850
# Remote branches can have a second reference to the old
851
# repository that need to be replaced.
852
if self._real_branch is not None:
853
self._real_branch.repository = new_repository
854
self.repository.lock_write(token=lock_token)
855
if lock_token is not None:
856
old_repository.leave_lock_in_place()
857
old_repository.unlock()
858
if lock_token is not None:
859
# XXX: self.repository.leave_lock_in_place() before this
860
# function will not be preserved. Fortunately that doesn't
861
# affect the current default format (2a), and would be a
862
# corner-case anyway.
863
# - Andrew Bennetts, 2010/06/30
864
self.repository.dont_leave_lock_in_place()
868
old_repository.unlock()
869
except errors.LockNotHeld:
872
if old_lock_count == 0:
873
raise AssertionError(
874
'old_repository should have been locked at least once.')
875
for i in range(old_lock_count-1):
876
self.repository.lock_write()
877
# Fetch from the old repository into the new.
878
old_repository.lock_read()
880
# XXX: If you unstack a branch while it has a working tree
881
# with a pending merge, the pending-merged revisions will no
882
# longer be present. You can (probably) revert and remerge.
884
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
885
except errors.TagsNotSupported:
886
tags_to_fetch = set()
887
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
888
old_repository, required_ids=[self.last_revision()],
889
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
890
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
892
old_repository.unlock()
896
def _set_tags_bytes(self, bytes):
897
"""Mirror method for _get_tags_bytes.
899
:seealso: Branch._get_tags_bytes.
901
return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
904
def _set_tags_bytes_locked(self, bytes):
905
self._tags_bytes = bytes
906
return self._transport.put_bytes('tags', bytes)
908
360
def _cache_revision_history(self, rev_history):
909
361
"""Set the cached revision history to rev_history.
1020
468
common_index = min(self_len, other_len) -1
1021
469
if common_index >= 0 and \
1022
470
self_history[common_index] != other_history[common_index]:
1023
raise errors.DivergedBranches(self, other)
471
raise DivergedBranches(self, other)
1025
473
if stop_revision is None:
1026
474
stop_revision = other_len
476
assert isinstance(stop_revision, int)
1028
477
if stop_revision > other_len:
1029
478
raise errors.NoSuchRevision(self, stop_revision)
1030
479
return other_history[self_len:stop_revision]
1032
def update_revisions(self, other, stop_revision=None, overwrite=False,
1033
graph=None, fetch_tags=True):
481
def update_revisions(self, other, stop_revision=None):
1034
482
"""Pull in new perfect-fit revisions.
1036
484
:param other: Another Branch to pull from
1037
485
:param stop_revision: Updated until the given revision
1038
:param overwrite: Always set the branch pointer, rather than checking
1039
to see if it is a proper descendant.
1040
:param graph: A Graph object that can be used to query history
1041
information. This can be None.
1042
:param fetch_tags: Flag that specifies if tags from other should be
1046
return InterBranch.get(other, self).update_revisions(stop_revision,
1047
overwrite, graph, fetch_tags=fetch_tags)
1049
@deprecated_method(deprecated_in((2, 4, 0)))
1050
def import_last_revision_info(self, source_repo, revno, revid):
1051
"""Set the last revision info, importing from another repo if necessary.
1053
:param source_repo: Source repository to optionally fetch from
1054
:param revno: Revision number of the new tip
1055
:param revid: Revision id of the new tip
1057
if not self.repository.has_same_location(source_repo):
1058
self.repository.fetch(source_repo, revision_id=revid)
1059
self.set_last_revision_info(revno, revid)
1061
def import_last_revision_info_and_tags(self, source, revno, revid):
1062
"""Set the last revision info, importing from another repo if necessary.
1064
This is used by the bound branch code to upload a revision to
1065
the master branch first before updating the tip of the local branch.
1066
Revisions referenced by source's tags are also transferred.
1068
:param source: Source branch to optionally fetch from
1069
:param revno: Revision number of the new tip
1070
:param revid: Revision id of the new tip
1072
if not self.repository.has_same_location(source.repository):
1074
tags_to_fetch = set(source.tags.get_reverse_tag_dict())
1075
except errors.TagsNotSupported:
1076
tags_to_fetch = set()
1077
fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
1078
source.repository, [revid],
1079
if_present_ids=tags_to_fetch).execute()
1080
self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1081
self.set_last_revision_info(revno, revid)
488
raise NotImplementedError(self.update_revisions)
1083
490
def revision_id_to_revno(self, revision_id):
1084
491
"""Given a revision id, return its revno"""
1090
497
except ValueError:
1091
498
raise errors.NoSuchRevision(self, revision_id)
1094
500
def get_rev_id(self, revno, history=None):
1095
501
"""Find the revision id of the specified revno."""
1097
503
return _mod_revision.NULL_REVISION
1098
last_revno, last_revid = self.last_revision_info()
1099
if revno == last_revno:
1101
if revno <= 0 or revno > last_revno:
505
history = self.revision_history()
506
if revno <= 0 or revno > len(history):
1102
507
raise errors.NoSuchRevision(self, revno)
1103
distance_from_last = last_revno - revno
1104
if len(self._partial_revision_history_cache) <= distance_from_last:
1105
self._extend_partial_history(distance_from_last)
1106
return self._partial_revision_history_cache[distance_from_last]
508
return history[revno - 1]
1108
510
def pull(self, source, overwrite=False, stop_revision=None,
1109
possible_transports=None, *args, **kwargs):
511
possible_transports=None):
1110
512
"""Mirror source into this branch.
1112
514
This branch is considered to be 'local', having low latency.
1114
516
:returns: PullResult instance
1116
return InterBranch.get(source, self).pull(overwrite=overwrite,
1117
stop_revision=stop_revision,
1118
possible_transports=possible_transports, *args, **kwargs)
518
raise NotImplementedError(self.pull)
1120
def push(self, target, overwrite=False, stop_revision=None, *args,
520
def push(self, target, overwrite=False, stop_revision=None):
1122
521
"""Mirror this branch into target.
1124
523
This branch is considered to be 'local', having low latency.
1126
return InterBranch.get(self, target).push(overwrite, stop_revision,
1129
def lossy_push(self, target, stop_revision=None):
1130
"""Push deltas into another branch.
1132
:note: This does not, like push, retain the revision ids from
1133
the source branch and will, rather than adding bzr-specific
1134
metadata, push only those semantics of the revision that can be
1135
natively represented by this branch' VCS.
1137
:param target: Target branch
1138
:param stop_revision: Revision to push, defaults to last revision.
1139
:return: BranchPushResult with an extra member revidmap:
1140
A dictionary mapping revision ids from the target branch
1141
to new revision ids in the target branch, for each
1142
revision that was pushed.
1144
inter = InterBranch.get(self, target)
1145
lossy_push = getattr(inter, "lossy_push", None)
1146
if lossy_push is None:
1147
raise errors.LossyPushToSameVCS(self, target)
1148
return lossy_push(stop_revision)
525
raise NotImplementedError(self.push)
1150
527
def basis_tree(self):
1151
528
"""Return `Tree` object for last revision."""
1152
529
return self.repository.revision_tree(self.last_revision())
531
def rename_one(self, from_rel, to_rel):
534
This can change the directory or the filename or both.
536
raise NotImplementedError(self.rename_one)
538
def move(self, from_paths, to_name):
541
to_name must exist as a versioned directory.
543
If to_name exists and is a directory, the files are moved into
544
it, keeping their old names. If it is a directory,
546
Note that to_name is only the last component of the new name;
547
this doesn't change the directory.
549
This returns a list of (from_path, to_path) pairs for each
552
raise NotImplementedError(self.move)
1154
554
def get_parent(self):
1155
555
"""Return the parent location of the branch.
1157
This is the default location for pull/missing. The usual
557
This is the default location for push/pull/missing. The usual
1158
558
pattern is that the user can override it by specifying a
1161
parent = self._get_parent_location()
1164
# This is an old-format absolute path to a local branch
1165
# turn it into a url
1166
if parent.startswith('/'):
1167
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1169
return urlutils.join(self.base[:-1], parent)
1170
except errors.InvalidURLJoin, e:
1171
raise errors.InaccessibleParent(parent, self.user_url)
1173
def _get_parent_location(self):
1174
raise NotImplementedError(self._get_parent_location)
561
raise NotImplementedError(self.get_parent)
1176
563
def _set_config_location(self, name, url, config=None,
1177
564
make_relative=False):
1278
642
self.check_real_revno(revno)
1280
644
def check_real_revno(self, revno):
1282
646
Check whether a revno corresponds to a real revision.
1283
647
Zero (the NULL revision) is considered invalid
1285
649
if revno < 1 or revno > self.revno():
1286
raise errors.InvalidRevisionNumber(revno)
650
raise InvalidRevisionNumber(revno)
1288
652
@needs_read_lock
1289
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
653
def clone(self, to_bzrdir, revision_id=None):
1290
654
"""Clone this branch into to_bzrdir preserving all semantic values.
1292
Most API users will want 'create_clone_on_transport', which creates a
1293
new bzrdir and branch on the fly.
1295
656
revision_id: if not None, the revision history in the new branch will
1296
657
be truncated to end with revision_id.
1298
result = to_bzrdir.create_branch()
1301
if repository_policy is not None:
1302
repository_policy.configure_branch(result)
1303
self.copy_content_into(result, revision_id=revision_id)
659
result = self._format.initialize(to_bzrdir)
660
self.copy_content_into(result, revision_id=revision_id)
1308
663
@needs_read_lock
1309
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
664
def sprout(self, to_bzrdir, revision_id=None):
1311
665
"""Create a new line of development from the branch, into to_bzrdir.
1313
to_bzrdir controls the branch format.
1315
667
revision_id: if not None, the revision history in the new branch will
1316
668
be truncated to end with revision_id.
1318
if (repository_policy is not None and
1319
repository_policy.requires_stacking()):
1320
to_bzrdir._format.require_stacking(_skip_repo=True)
1321
result = to_bzrdir.create_branch(repository=repository)
1324
if repository_policy is not None:
1325
repository_policy.configure_branch(result)
1326
self.copy_content_into(result, revision_id=revision_id)
1327
result.set_parent(self.bzrdir.root_transport.base)
670
result = self._format.initialize(to_bzrdir)
671
self.copy_content_into(result, revision_id=revision_id)
672
result.set_parent(self.bzrdir.root_transport.base)
1332
675
def _synchronize_history(self, destination, revision_id):
1333
676
"""Synchronize last revision and revision history between branches.
1335
678
This version is most efficient when the destination is also a
1336
BzrBranch6, but works for BzrBranch5, as long as the destination's
1337
repository contains all the lefthand ancestors of the intended
1338
last_revision. If not, set_last_revision_info will fail.
679
BzrBranch5, but works for BzrBranch6 as long as the revision
680
history is the true lefthand parent history, and all of the revisions
681
are in the destination's repository. If not, set_revision_history
1340
684
:param destination: The branch to copy the history into
1341
685
:param revision_id: The revision-id to truncate history at. May
1342
686
be None to copy complete history.
1344
source_revno, source_revision_id = self.last_revision_info()
1345
if revision_id is None:
1346
revno, revision_id = source_revno, source_revision_id
1348
graph = self.repository.get_graph()
688
if revision_id == _mod_revision.NULL_REVISION:
690
new_history = self.revision_history()
691
if revision_id is not None and new_history != []:
1350
revno = graph.find_distance_to_null(revision_id,
1351
[(source_revision_id, source_revno)])
1352
except errors.GhostRevisionsHaveNoRevno:
1353
# Default to 1, if we can't find anything else
1355
destination.set_last_revision_info(revno, revision_id)
693
new_history = new_history[:new_history.index(revision_id) + 1]
695
rev = self.repository.get_revision(revision_id)
696
new_history = rev.get_history(self.repository)[1:]
697
destination.set_revision_history(new_history)
1357
700
def copy_content_into(self, destination, revision_id=None):
1358
701
"""Copy the content of self into destination.
1360
703
revision_id: if not None, the revision history in the new branch will
1361
704
be truncated to end with revision_id.
1363
return InterBranch.get(self, destination).copy_content_into(
1364
revision_id=revision_id)
1366
def update_references(self, target):
1367
if not getattr(self._format, 'supports_reference_locations', False):
1369
reference_dict = self._get_all_reference_info()
1370
if len(reference_dict) == 0:
1372
old_base = self.base
1373
new_base = target.base
1374
target_reference_dict = target._get_all_reference_info()
1375
for file_id, (tree_path, branch_location) in (
1376
reference_dict.items()):
1377
branch_location = urlutils.rebase_url(branch_location,
1379
target_reference_dict.setdefault(
1380
file_id, (tree_path, branch_location))
1381
target._set_all_reference_info(target_reference_dict)
706
self._synchronize_history(destination, revision_id)
708
parent = self.get_parent()
709
except errors.InaccessibleParent, e:
710
mutter('parent was not accessible to copy: %s', e)
713
destination.set_parent(parent)
714
self.tags.merge_to(destination.tags)
1383
716
@needs_read_lock
1384
def check(self, refs):
1385
718
"""Check consistency of the branch.
1387
720
In particular this checks that revisions given in the revision-history
1388
do actually match up in the revision graph, and that they're all
721
do actually match up in the revision graph, and that they're all
1389
722
present in the repository.
1391
724
Callers will typically also want to check the repository.
1393
:param refs: Calculated refs for this branch as specified by
1394
branch._get_check_refs()
1395
726
:return: A BranchCheckResult.
1397
result = BranchCheckResult(self)
1398
last_revno, last_revision_id = self.last_revision_info()
1399
actual_revno = refs[('lefthand-distance', last_revision_id)]
1400
if actual_revno != last_revno:
1401
result.errors.append(errors.BzrCheckError(
1402
'revno does not match len(mainline) %s != %s' % (
1403
last_revno, actual_revno)))
1404
# TODO: We should probably also check that self.revision_history
1405
# matches the repository for older branch formats.
1406
# If looking for the code that cross-checks repository parents against
1407
# the iter_reverse_revision_history output, that is now a repository
728
mainline_parent_id = None
729
for revision_id in self.revision_history():
731
revision = self.repository.get_revision(revision_id)
732
except errors.NoSuchRevision, e:
733
raise errors.BzrCheckError("mainline revision {%s} not in repository"
735
# In general the first entry on the revision history has no parents.
736
# But it's not illegal for it to have parents listed; this can happen
737
# in imports from Arch when the parents weren't reachable.
738
if mainline_parent_id is not None:
739
if mainline_parent_id not in revision.parent_ids:
740
raise errors.BzrCheckError("previous revision {%s} not listed among "
742
% (mainline_parent_id, revision_id))
743
mainline_parent_id = revision_id
744
return BranchCheckResult(self)
1411
746
def _get_checkout_format(self):
1412
747
"""Return the most suitable metadir for a checkout of this branch.
1413
748
Weaves are used if this branch's repository uses weaves.
1415
format = self.repository.bzrdir.checkout_metadir()
1416
format.set_branch_format(self._format)
750
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
751
from bzrlib.repofmt import weaverepo
752
format = bzrdir.BzrDirMetaFormat1()
753
format.repository_format = weaverepo.RepositoryFormat7()
755
format = self.repository.bzrdir.checkout_metadir()
756
format.set_branch_format(self._format)
1419
def create_clone_on_transport(self, to_transport, revision_id=None,
1420
stacked_on=None, create_prefix=False, use_existing_dir=False,
1422
"""Create a clone of this branch and its bzrdir.
1424
:param to_transport: The transport to clone onto.
1425
:param revision_id: The revision id to use as tip in the new branch.
1426
If None the tip is obtained from this branch.
1427
:param stacked_on: An optional URL to stack the clone on.
1428
:param create_prefix: Create any missing directories leading up to
1430
:param use_existing_dir: Use an existing directory if one exists.
1432
# XXX: Fix the bzrdir API to allow getting the branch back from the
1433
# clone call. Or something. 20090224 RBC/spiv.
1434
# XXX: Should this perhaps clone colocated branches as well,
1435
# rather than just the default branch? 20100319 JRV
1436
if revision_id is None:
1437
revision_id = self.last_revision()
1438
dir_to = self.bzrdir.clone_on_transport(to_transport,
1439
revision_id=revision_id, stacked_on=stacked_on,
1440
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1442
return dir_to.open_branch()
1444
759
def create_checkout(self, to_location, revision_id=None,
1445
lightweight=False, accelerator_tree=None,
1447
761
"""Create a checkout of a branch.
1449
763
:param to_location: The url to produce the checkout at
1450
764
:param revision_id: The revision to check out
1451
765
:param lightweight: If True, produce a lightweight checkout, otherwise,
1452
766
produce a bound branch (heavyweight checkout)
1453
:param accelerator_tree: A tree which can be used for retrieving file
1454
contents more quickly than the revision tree, i.e. a workingtree.
1455
The revision tree will be used for cases where accelerator_tree's
1456
content is different.
1457
:param hardlink: If true, hard-link files from accelerator_tree,
1459
767
:return: The tree of the created checkout
1461
769
t = transport.get_transport(to_location)
1491
796
basis_tree.unlock()
1495
def reconcile(self, thorough=True):
1496
"""Make sure the data stored in this branch is consistent."""
1497
from bzrlib.reconcile import BranchReconciler
1498
reconciler = BranchReconciler(self, thorough=thorough)
1499
reconciler.reconcile()
1502
def reference_parent(self, file_id, path, possible_transports=None):
799
def reference_parent(self, file_id, path):
1503
800
"""Return the parent branch for a tree-reference file_id
1504
801
:param file_id: The file_id of the tree reference
1505
802
:param path: The path of the file_id in the tree
1506
803
:return: A branch associated with the file_id
1508
805
# FIXME should provide multiple branches, based on config
1509
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1510
possible_transports=possible_transports)
806
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1512
808
def supports_tags(self):
1513
809
return self._format.supports_tags()
1515
def automatic_tag_name(self, revision_id):
1516
"""Try to automatically find the tag name for a revision.
1518
:param revision_id: Revision id of the revision.
1519
:return: A tag name or None if no tag name could be determined.
1521
for hook in Branch.hooks['automatic_tag_name']:
1522
ret = hook(self, revision_id)
1527
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1529
"""Ensure that revision_b is a descendant of revision_a.
1531
This is a helper function for update_revisions.
1533
:raises: DivergedBranches if revision_b has diverged from revision_a.
1534
:returns: True if revision_b is a descendant of revision_a.
1536
relation = self._revision_relations(revision_a, revision_b, graph)
1537
if relation == 'b_descends_from_a':
1539
elif relation == 'diverged':
1540
raise errors.DivergedBranches(self, other_branch)
1541
elif relation == 'a_descends_from_b':
1544
raise AssertionError("invalid relation: %r" % (relation,))
1546
def _revision_relations(self, revision_a, revision_b, graph):
1547
"""Determine the relationship between two revisions.
1549
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1551
heads = graph.heads([revision_a, revision_b])
1552
if heads == set([revision_b]):
1553
return 'b_descends_from_a'
1554
elif heads == set([revision_a, revision_b]):
1555
# These branches have diverged
1557
elif heads == set([revision_a]):
1558
return 'a_descends_from_b'
1560
raise AssertionError("invalid heads: %r" % (heads,))
1563
812
class BranchFormat(object):
1564
813
"""An encapsulation of the initialization and open routines for a format.
1696
911
control_files = lockable_files.LockableFiles(branch_transport,
1697
912
lock_name, lock_class)
1698
913
control_files.create_lock()
1700
control_files.lock_write()
1701
except errors.LockContention:
1702
if lock_type != 'branch4':
914
control_files.lock_write()
1708
utf8_files += [('format', self.get_format_string())]
916
control_files.put_utf8('format', self.get_format_string())
1710
for (filename, content) in utf8_files:
1711
branch_transport.put_bytes(
1713
mode=a_bzrdir._get_file_mode())
918
for file, content in utf8_files:
919
control_files.put_utf8(file, content)
1716
control_files.unlock()
1717
branch = self.open(a_bzrdir, name, _found=True,
1718
found_repository=repository)
1719
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
921
control_files.unlock()
922
return self.open(a_bzrdir, _found=True)
1722
def initialize(self, a_bzrdir, name=None, repository=None):
1723
"""Create a branch of this format in a_bzrdir.
1725
:param name: Name of the colocated branch to create.
924
def initialize(self, a_bzrdir):
925
"""Create a branch of this format in a_bzrdir."""
1727
926
raise NotImplementedError(self.initialize)
1729
928
def is_supported(self):
1730
929
"""Is this format supported?
1732
931
Supported formats can be initialized and opened.
1733
Unsupported formats may not support initialization or committing or
932
Unsupported formats may not support initialization or committing or
1734
933
some other features depending on the reason for not being supported.
1738
def make_tags(self, branch):
1739
"""Create a tags object for branch.
1741
This method is on BranchFormat, because BranchFormats are reflected
1742
over the wire via network_name(), whereas full Branch instances require
1743
multiple VFS method calls to operate at all.
1745
The default implementation returns a disabled-tags instance.
1747
Note that it is normal for branch to be a RemoteBranch when using tags
1750
return DisabledTags(branch)
1752
def network_name(self):
1753
"""A simple byte string uniquely identifying this format for RPC calls.
1755
MetaDir branch formats use their disk format string to identify the
1756
repository over the wire. All in one formats such as bzr < 0.8, and
1757
foreign formats like svn/git and hg should use some marker which is
1758
unique and immutable.
1760
raise NotImplementedError(self.network_name)
1762
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1763
found_repository=None):
937
def open(self, a_bzrdir, _found=False):
1764
938
"""Return the branch object for a_bzrdir
1766
:param a_bzrdir: A BzrDir that contains a branch.
1767
:param name: Name of colocated branch to open
1768
:param _found: a private parameter, do not use it. It is used to
1769
indicate if format probing has already be done.
1770
:param ignore_fallbacks: when set, no fallback branches will be opened
1771
(if there are any). Default is to open fallbacks.
940
_found is a private parameter, do not use it. It is used to indicate
941
if format probing has already be done.
1773
943
raise NotImplementedError(self.open)
1776
def register_extra_format(klass, format):
1777
"""Register a branch format that can not be part of a metadir.
1779
This is mainly useful to allow custom branch formats, such as
1780
older Bazaar formats and foreign formats, to be tested
1782
klass._extra_formats.append(format)
1783
network_format_registry.register(
1784
format.network_name(), format.__class__)
1787
946
def register_format(klass, format):
1788
"""Register a metadir format.
1790
See MetaDirBranchFormatFactory for the ability to register a format
1791
without loading the code the format needs until it is actually used.
1793
947
klass._formats[format.get_format_string()] = format
1794
# Metadir formats have a network name of their format string, and get
1795
# registered as factories.
1796
if isinstance(format, MetaDirBranchFormatFactory):
1797
network_format_registry.register(format.get_format_string(), format)
1799
network_format_registry.register(format.get_format_string(),
1803
950
def set_default_format(klass, format):
1804
951
klass._default_format = format
1806
def supports_set_append_revisions_only(self):
1807
"""True if this format supports set_append_revisions_only."""
1810
def supports_stacking(self):
1811
"""True if this format records a stacked-on branch."""
1815
954
def unregister_format(klass, format):
955
assert klass._formats[format.get_format_string()] is format
1816
956
del klass._formats[format.get_format_string()]
1819
def unregister_extra_format(klass, format):
1820
klass._extra_formats.remove(format)
1822
958
def __str__(self):
1823
return self.get_format_description().rstrip()
959
return self.get_format_string().rstrip()
1825
961
def supports_tags(self):
1826
962
"""True if this format supports tags stored in the branch"""
1827
963
return False # by default
1830
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1831
"""A factory for a BranchFormat object, permitting simple lazy registration.
1833
While none of the built in BranchFormats are lazy registered yet,
1834
bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1835
use it, and the bzr-loom plugin uses it as well (see
1836
bzrlib.plugins.loom.formats).
1839
def __init__(self, format_string, module_name, member_name):
1840
"""Create a MetaDirBranchFormatFactory.
1842
:param format_string: The format string the format has.
1843
:param module_name: Module to load the format class from.
1844
:param member_name: Attribute name within the module for the format class.
1846
registry._LazyObjectGetter.__init__(self, module_name, member_name)
1847
self._format_string = format_string
1849
def get_format_string(self):
1850
"""See BranchFormat.get_format_string."""
1851
return self._format_string
1854
"""Used for network_format_registry support."""
1855
return self.get_obj()()
965
# XXX: Probably doesn't really belong here -- mbp 20070212
966
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
968
branch_transport = a_bzrdir.get_branch_transport(self)
969
control_files = lockable_files.LockableFiles(branch_transport,
970
lock_filename, lock_class)
971
control_files.create_lock()
972
control_files.lock_write()
974
for filename, content in utf8_files:
975
control_files.put_utf8(filename, content)
977
control_files.unlock()
1858
980
class BranchHooks(Hooks):
1859
981
"""A dictionary mapping hook name to a list of callables for branch hooks.
1861
983
e.g. ['set_rh'] Is the list of items to be called when the
1862
984
set_revision_history function is invoked.
1871
993
Hooks.__init__(self)
1872
self.create_hook(HookPoint('set_rh',
1873
"Invoked whenever the revision history has been set via "
1874
"set_revision_history. The api signature is (branch, "
1875
"revision_history), and the branch will be write-locked. "
1876
"The set_rh hook can be expensive for bzr to trigger, a better "
1877
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1878
self.create_hook(HookPoint('open',
1879
"Called with the Branch object that has been opened after a "
1880
"branch is opened.", (1, 8), None))
1881
self.create_hook(HookPoint('post_push',
1882
"Called after a push operation completes. post_push is called "
1883
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1884
"bzr client.", (0, 15), None))
1885
self.create_hook(HookPoint('post_pull',
1886
"Called after a pull operation completes. post_pull is called "
1887
"with a bzrlib.branch.PullResult object and only runs in the "
1888
"bzr client.", (0, 15), None))
1889
self.create_hook(HookPoint('pre_commit',
1890
"Called after a commit is calculated but before it is "
1891
"completed. pre_commit is called with (local, master, old_revno, "
1892
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1893
"). old_revid is NULL_REVISION for the first commit to a branch, "
1894
"tree_delta is a TreeDelta object describing changes from the "
1895
"basis revision. hooks MUST NOT modify this delta. "
1896
" future_tree is an in-memory tree obtained from "
1897
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1898
"tree.", (0,91), None))
1899
self.create_hook(HookPoint('post_commit',
1900
"Called in the bzr client after a commit has completed. "
1901
"post_commit is called with (local, master, old_revno, old_revid, "
1902
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1903
"commit to a branch.", (0, 15), None))
1904
self.create_hook(HookPoint('post_uncommit',
1905
"Called in the bzr client after an uncommit completes. "
1906
"post_uncommit is called with (local, master, old_revno, "
1907
"old_revid, new_revno, new_revid) where local is the local branch "
1908
"or None, master is the target branch, and an empty branch "
1909
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1910
self.create_hook(HookPoint('pre_change_branch_tip',
1911
"Called in bzr client and server before a change to the tip of a "
1912
"branch is made. pre_change_branch_tip is called with a "
1913
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1914
"commit, uncommit will all trigger this hook.", (1, 6), None))
1915
self.create_hook(HookPoint('post_change_branch_tip',
1916
"Called in bzr client and server after a change to the tip of a "
1917
"branch is made. post_change_branch_tip is called with a "
1918
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1919
"commit, uncommit will all trigger this hook.", (1, 4), None))
1920
self.create_hook(HookPoint('transform_fallback_location',
1921
"Called when a stacked branch is activating its fallback "
1922
"locations. transform_fallback_location is called with (branch, "
1923
"url), and should return a new url. Returning the same url "
1924
"allows it to be used as-is, returning a different one can be "
1925
"used to cause the branch to stack on a closer copy of that "
1926
"fallback_location. Note that the branch cannot have history "
1927
"accessing methods called on it during this hook because the "
1928
"fallback locations have not been activated. When there are "
1929
"multiple hooks installed for transform_fallback_location, "
1930
"all are called with the url returned from the previous hook."
1931
"The order is however undefined.", (1, 9), None))
1932
self.create_hook(HookPoint('automatic_tag_name',
1933
"Called to determine an automatic tag name for a revision. "
1934
"automatic_tag_name is called with (branch, revision_id) and "
1935
"should return a tag name or None if no tag name could be "
1936
"determined. The first non-None tag name returned will be used.",
1938
self.create_hook(HookPoint('post_branch_init',
1939
"Called after new branch initialization completes. "
1940
"post_branch_init is called with a "
1941
"bzrlib.branch.BranchInitHookParams. "
1942
"Note that init, branch and checkout (both heavyweight and "
1943
"lightweight) will all trigger this hook.", (2, 2), None))
1944
self.create_hook(HookPoint('post_switch',
1945
"Called after a checkout switches branch. "
1946
"post_switch is called with a "
1947
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
994
# Introduced in 0.15:
995
# invoked whenever the revision history has been set
996
# with set_revision_history. The api signature is
997
# (branch, revision_history), and the branch will
1000
# invoked after a push operation completes.
1001
# the api signature is
1003
# containing the members
1004
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1005
# where local is the local target branch or None, master is the target
1006
# master branch, and the rest should be self explanatory. The source
1007
# is read locked and the target branches write locked. Source will
1008
# be the local low-latency branch.
1009
self['post_push'] = []
1010
# invoked after a pull operation completes.
1011
# the api signature is
1013
# containing the members
1014
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1015
# where local is the local branch or None, master is the target
1016
# master branch, and the rest should be self explanatory. The source
1017
# is read locked and the target branches write locked. The local
1018
# branch is the low-latency branch.
1019
self['post_pull'] = []
1020
# invoked before a commit operation takes place.
1021
# the api signature is
1022
# (local, master, old_revno, old_revid, future_revno, future_revid,
1023
# tree_delta, future_tree).
1024
# old_revid is NULL_REVISION for the first commit to a branch
1025
# tree_delta is a TreeDelta object describing changes from the basis
1026
# revision, hooks MUST NOT modify this delta
1027
# future_tree is an in-memory tree obtained from
1028
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1029
self['pre_commit'] = []
1030
# invoked after a commit operation completes.
1031
# the api signature is
1032
# (local, master, old_revno, old_revid, new_revno, new_revid)
1033
# old_revid is NULL_REVISION for the first commit to a branch.
1034
self['post_commit'] = []
1035
# invoked after a uncommit operation completes.
1036
# the api signature is
1037
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1038
# local is the local branch or None, master is the target branch,
1039
# and an empty branch recieves new_revno of 0, new_revid of None.
1040
self['post_uncommit'] = []
1951
1043
# install the default hooks into the Branch class.
1952
1044
Branch.hooks = BranchHooks()
1955
class ChangeBranchTipParams(object):
1956
"""Object holding parameters passed to *_change_branch_tip hooks.
1958
There are 5 fields that hooks may wish to access:
1960
:ivar branch: the branch being changed
1961
:ivar old_revno: revision number before the change
1962
:ivar new_revno: revision number after the change
1963
:ivar old_revid: revision id before the change
1964
:ivar new_revid: revision id after the change
1966
The revid fields are strings. The revno fields are integers.
1969
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1970
"""Create a group of ChangeBranchTip parameters.
1972
:param branch: The branch being changed.
1973
:param old_revno: Revision number before the change.
1974
:param new_revno: Revision number after the change.
1975
:param old_revid: Tip revision id before the change.
1976
:param new_revid: Tip revision id after the change.
1978
self.branch = branch
1979
self.old_revno = old_revno
1980
self.new_revno = new_revno
1981
self.old_revid = old_revid
1982
self.new_revid = new_revid
1984
def __eq__(self, other):
1985
return self.__dict__ == other.__dict__
1988
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1989
self.__class__.__name__, self.branch,
1990
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1993
class BranchInitHookParams(object):
1994
"""Object holding parameters passed to *_branch_init hooks.
1996
There are 4 fields that hooks may wish to access:
1998
:ivar format: the branch format
1999
:ivar bzrdir: the BzrDir where the branch will be/has been initialized
2000
:ivar name: name of colocated branch, if any (or None)
2001
:ivar branch: the branch created
2003
Note that for lightweight checkouts, the bzrdir and format fields refer to
2004
the checkout, hence they are different from the corresponding fields in
2005
branch, which refer to the original branch.
2008
def __init__(self, format, a_bzrdir, name, branch):
2009
"""Create a group of BranchInitHook parameters.
2011
:param format: the branch format
2012
:param a_bzrdir: the BzrDir where the branch will be/has been
2014
:param name: name of colocated branch, if any (or None)
2015
:param branch: the branch created
2017
Note that for lightweight checkouts, the bzrdir and format fields refer
2018
to the checkout, hence they are different from the corresponding fields
2019
in branch, which refer to the original branch.
2021
self.format = format
2022
self.bzrdir = a_bzrdir
2024
self.branch = branch
2026
def __eq__(self, other):
2027
return self.__dict__ == other.__dict__
2030
return "<%s of %s>" % (self.__class__.__name__, self.branch)
2033
class SwitchHookParams(object):
2034
"""Object holding parameters passed to *_switch hooks.
2036
There are 4 fields that hooks may wish to access:
2038
:ivar control_dir: BzrDir of the checkout to change
2039
:ivar to_branch: branch that the checkout is to reference
2040
:ivar force: skip the check for local commits in a heavy checkout
2041
:ivar revision_id: revision ID to switch to (or None)
2044
def __init__(self, control_dir, to_branch, force, revision_id):
2045
"""Create a group of SwitchHook parameters.
2047
:param control_dir: BzrDir of the checkout to change
2048
:param to_branch: branch that the checkout is to reference
2049
:param force: skip the check for local commits in a heavy checkout
2050
:param revision_id: revision ID to switch to (or None)
2052
self.control_dir = control_dir
2053
self.to_branch = to_branch
2055
self.revision_id = revision_id
2057
def __eq__(self, other):
2058
return self.__dict__ == other.__dict__
2061
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
2062
self.control_dir, self.to_branch,
2066
1047
class BzrBranchFormat4(BranchFormat):
2067
1048
"""Bzr branch format 4.
2075
1056
"""See BranchFormat.get_format_description()."""
2076
1057
return "Branch format 4"
2078
def initialize(self, a_bzrdir, name=None, repository=None):
1059
def initialize(self, a_bzrdir):
2079
1060
"""Create a branch of this format in a_bzrdir."""
2080
if repository is not None:
2081
raise NotImplementedError(
2082
"initialize(repository=<not None>) on %r" % (self,))
2083
1061
utf8_files = [('revision-history', ''),
2084
1062
('branch-name', ''),
2086
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1064
return self._initialize_helper(a_bzrdir, utf8_files,
2087
1065
lock_type='branch4', set_format=False)
2089
1067
def __init__(self):
2090
1068
super(BzrBranchFormat4, self).__init__()
2091
1069
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2093
def network_name(self):
2094
"""The network name for this format is the control dirs disk label."""
2095
return self._matchingbzrdir.get_format_string()
1071
def open(self, a_bzrdir, _found=False):
1072
"""Return the branch object for a_bzrdir
2097
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2098
found_repository=None):
2099
"""See BranchFormat.open()."""
1074
_found is a private parameter, do not use it. It is used to indicate
1075
if format probing has already be done.
2101
1078
# we are being called directly and must probe.
2102
1079
raise NotImplementedError
2103
if found_repository is None:
2104
found_repository = a_bzrdir.open_repository()
2105
return BzrBranchPreSplitOut(_format=self,
1080
return BzrBranch(_format=self,
2106
1081
_control_files=a_bzrdir._control_files,
2107
1082
a_bzrdir=a_bzrdir,
2109
_repository=found_repository)
1083
_repository=a_bzrdir.open_repository())
2111
1085
def __str__(self):
2112
1086
return "Bazaar-NG branch format 4"
2115
class BranchFormatMetadir(BranchFormat):
2116
"""Common logic for meta-dir based branch formats."""
2118
def _branch_class(self):
2119
"""What class to instantiate on open calls."""
2120
raise NotImplementedError(self._branch_class)
2122
def network_name(self):
2123
"""A simple byte string uniquely identifying this format for RPC calls.
2125
Metadir branch formats use their format string.
1089
class BzrBranchFormat5(BranchFormat):
1090
"""Bzr branch format 5.
1093
- a revision-history file.
1095
- a lock dir guarding the branch itself
1096
- all of this stored in a branch/ subdirectory
1097
- works with shared repositories.
1099
This format is new in bzr 0.8.
1102
def get_format_string(self):
1103
"""See BranchFormat.get_format_string()."""
1104
return "Bazaar-NG branch format 5\n"
1106
def get_format_description(self):
1107
"""See BranchFormat.get_format_description()."""
1108
return "Branch format 5"
1110
def initialize(self, a_bzrdir):
1111
"""Create a branch of this format in a_bzrdir."""
1112
utf8_files = [('revision-history', ''),
1113
('branch-name', ''),
1115
return self._initialize_helper(a_bzrdir, utf8_files)
1118
super(BzrBranchFormat5, self).__init__()
1119
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1121
def open(self, a_bzrdir, _found=False):
1122
"""Return the branch object for a_bzrdir
1124
_found is a private parameter, do not use it. It is used to indicate
1125
if format probing has already be done.
2127
return self.get_format_string()
2129
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2130
found_repository=None):
2131
"""See BranchFormat.open()."""
2133
format = BranchFormat.find_format(a_bzrdir, name=name)
2134
if format.__class__ != self.__class__:
2135
raise AssertionError("wrong format %r found for %r" %
2137
transport = a_bzrdir.get_branch_transport(None, name=name)
1128
format = BranchFormat.find_format(a_bzrdir)
1129
assert format.__class__ == self.__class__
1131
transport = a_bzrdir.get_branch_transport(None)
2139
1132
control_files = lockable_files.LockableFiles(transport, 'lock',
2140
1133
lockdir.LockDir)
2141
if found_repository is None:
2142
found_repository = a_bzrdir.find_repository()
2143
return self._branch_class()(_format=self,
1134
return BzrBranch5(_format=self,
2144
1135
_control_files=control_files,
2146
1136
a_bzrdir=a_bzrdir,
2147
_repository=found_repository,
2148
ignore_fallbacks=ignore_fallbacks)
2149
except errors.NoSuchFile:
2150
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2153
super(BranchFormatMetadir, self).__init__()
2154
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2155
self._matchingbzrdir.set_branch_format(self)
2157
def supports_tags(self):
2161
class BzrBranchFormat5(BranchFormatMetadir):
2162
"""Bzr branch format 5.
2165
- a revision-history file.
2167
- a lock dir guarding the branch itself
2168
- all of this stored in a branch/ subdirectory
2169
- works with shared repositories.
2171
This format is new in bzr 0.8.
2174
def _branch_class(self):
2177
def get_format_string(self):
2178
"""See BranchFormat.get_format_string()."""
2179
return "Bazaar-NG branch format 5\n"
2181
def get_format_description(self):
2182
"""See BranchFormat.get_format_description()."""
2183
return "Branch format 5"
2185
def initialize(self, a_bzrdir, name=None, repository=None):
2186
"""Create a branch of this format in a_bzrdir."""
2187
utf8_files = [('revision-history', ''),
2188
('branch-name', ''),
2190
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2192
def supports_tags(self):
2196
class BzrBranchFormat6(BranchFormatMetadir):
1137
_repository=a_bzrdir.find_repository())
1139
raise NotBranchError(path=transport.base)
1142
class BzrBranchFormat6(BzrBranchFormat5):
2197
1143
"""Branch format with last-revision and tags.
2199
1145
Unlike previous formats, this has no explicit revision history. Instead,
2215
1158
"""See BranchFormat.get_format_description()."""
2216
1159
return "Branch format 6"
2218
def initialize(self, a_bzrdir, name=None, repository=None):
2219
"""Create a branch of this format in a_bzrdir."""
2220
utf8_files = [('last-revision', '0 null:\n'),
2221
('branch.conf', ''),
2224
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2226
def make_tags(self, branch):
2227
"""See bzrlib.branch.BranchFormat.make_tags()."""
2228
return BasicTags(branch)
2230
def supports_set_append_revisions_only(self):
2234
class BzrBranchFormat8(BranchFormatMetadir):
2235
"""Metadir format supporting storing locations of subtree branches."""
2237
def _branch_class(self):
2240
def get_format_string(self):
2241
"""See BranchFormat.get_format_string()."""
2242
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2244
def get_format_description(self):
2245
"""See BranchFormat.get_format_description()."""
2246
return "Branch format 8"
2248
def initialize(self, a_bzrdir, name=None, repository=None):
2249
"""Create a branch of this format in a_bzrdir."""
2250
utf8_files = [('last-revision', '0 null:\n'),
2251
('branch.conf', ''),
2255
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2258
super(BzrBranchFormat8, self).__init__()
2259
self._matchingbzrdir.repository_format = \
2260
RepositoryFormatKnitPack5RichRoot()
2262
def make_tags(self, branch):
2263
"""See bzrlib.branch.BranchFormat.make_tags()."""
2264
return BasicTags(branch)
2266
def supports_set_append_revisions_only(self):
2269
def supports_stacking(self):
2272
supports_reference_locations = True
2275
class BzrBranchFormat7(BzrBranchFormat8):
2276
"""Branch format with last-revision, tags, and a stacked location pointer.
2278
The stacked location pointer is passed down to the repository and requires
2279
a repository format with supports_external_lookups = True.
2281
This format was introduced in bzr 1.6.
2284
def initialize(self, a_bzrdir, name=None, repository=None):
2285
"""Create a branch of this format in a_bzrdir."""
2286
utf8_files = [('last-revision', '0 null:\n'),
2287
('branch.conf', ''),
2290
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2292
def _branch_class(self):
2295
def get_format_string(self):
2296
"""See BranchFormat.get_format_string()."""
2297
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2299
def get_format_description(self):
2300
"""See BranchFormat.get_format_description()."""
2301
return "Branch format 7"
2303
def supports_set_append_revisions_only(self):
2306
supports_reference_locations = False
1161
def initialize(self, a_bzrdir):
1162
"""Create a branch of this format in a_bzrdir."""
1163
utf8_files = [('last-revision', '0 null:\n'),
1164
('branch.conf', ''),
1167
return self._initialize_helper(a_bzrdir, utf8_files)
1169
def open(self, a_bzrdir, _found=False):
1170
"""Return the branch object for a_bzrdir
1172
_found is a private parameter, do not use it. It is used to indicate
1173
if format probing has already be done.
1176
format = BranchFormat.find_format(a_bzrdir)
1177
assert format.__class__ == self.__class__
1178
transport = a_bzrdir.get_branch_transport(None)
1179
control_files = lockable_files.LockableFiles(transport, 'lock',
1181
return BzrBranch6(_format=self,
1182
_control_files=control_files,
1184
_repository=a_bzrdir.find_repository())
1186
def supports_tags(self):
2309
1190
class BranchReferenceFormat(BranchFormat):
2325
1206
"""See BranchFormat.get_format_description()."""
2326
1207
return "Checkout reference format 1"
2328
def get_reference(self, a_bzrdir, name=None):
1209
def get_reference(self, a_bzrdir):
2329
1210
"""See BranchFormat.get_reference()."""
2330
transport = a_bzrdir.get_branch_transport(None, name=name)
2331
return transport.get_bytes('location')
1211
transport = a_bzrdir.get_branch_transport(None)
1212
return transport.get('location').read()
2333
def set_reference(self, a_bzrdir, name, to_branch):
1214
def set_reference(self, a_bzrdir, to_branch):
2334
1215
"""See BranchFormat.set_reference()."""
2335
transport = a_bzrdir.get_branch_transport(None, name=name)
1216
transport = a_bzrdir.get_branch_transport(None)
2336
1217
location = transport.put_bytes('location', to_branch.base)
2338
def initialize(self, a_bzrdir, name=None, target_branch=None,
1219
def initialize(self, a_bzrdir, target_branch=None):
2340
1220
"""Create a branch of this format in a_bzrdir."""
2341
1221
if target_branch is None:
2342
1222
# this format does not implement branch itself, thus the implicit
2343
1223
# creation contract must see it as uninitializable
2344
1224
raise errors.UninitializableFormat(self)
2345
mutter('creating branch reference in %s', a_bzrdir.user_url)
2346
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1225
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1226
branch_transport = a_bzrdir.get_branch_transport(self)
2347
1227
branch_transport.put_bytes('location',
2348
target_branch.bzrdir.user_url)
1228
target_branch.bzrdir.root_transport.base)
2349
1229
branch_transport.put_bytes('format', self.get_format_string())
2351
a_bzrdir, name, _found=True,
1231
a_bzrdir, _found=True,
2352
1232
possible_transports=[target_branch.bzrdir.root_transport])
2353
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2356
1234
def __init__(self):
2357
1235
super(BranchReferenceFormat, self).__init__()
2358
1236
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2359
self._matchingbzrdir.set_branch_format(self)
2361
1238
def _make_reference_clone_function(format, a_branch):
2362
1239
"""Create a clone() routine for a branch dynamically."""
2363
def clone(to_bzrdir, revision_id=None,
2364
repository_policy=None):
1240
def clone(to_bzrdir, revision_id=None):
2365
1241
"""See Branch.clone()."""
2366
return format.initialize(to_bzrdir, target_branch=a_branch)
1242
return format.initialize(to_bzrdir, a_branch)
2367
1243
# cannot obey revision_id limits when cloning a reference ...
2368
1244
# FIXME RBC 20060210 either nuke revision_id for clone, or
2369
1245
# emit some sort of warning/error to the caller ?!
2372
def open(self, a_bzrdir, name=None, _found=False, location=None,
2373
possible_transports=None, ignore_fallbacks=False,
2374
found_repository=None):
1248
def open(self, a_bzrdir, _found=False, location=None,
1249
possible_transports=None):
2375
1250
"""Return the branch that the branch reference in a_bzrdir points at.
2377
:param a_bzrdir: A BzrDir that contains a branch.
2378
:param name: Name of colocated branch to open, if any
2379
:param _found: a private parameter, do not use it. It is used to
2380
indicate if format probing has already be done.
2381
:param ignore_fallbacks: when set, no fallback branches will be opened
2382
(if there are any). Default is to open fallbacks.
2383
:param location: The location of the referenced branch. If
2384
unspecified, this will be determined from the branch reference in
2386
:param possible_transports: An optional reusable transports list.
1252
_found is a private parameter, do not use it. It is used to indicate
1253
if format probing has already be done.
2389
format = BranchFormat.find_format(a_bzrdir, name=name)
2390
if format.__class__ != self.__class__:
2391
raise AssertionError("wrong format %r found for %r" %
1256
format = BranchFormat.find_format(a_bzrdir)
1257
assert format.__class__ == self.__class__
2393
1258
if location is None:
2394
location = self.get_reference(a_bzrdir, name)
1259
location = self.get_reference(a_bzrdir)
2395
1260
real_bzrdir = bzrdir.BzrDir.open(
2396
1261
location, possible_transports=possible_transports)
2397
result = real_bzrdir.open_branch(name=name,
2398
ignore_fallbacks=ignore_fallbacks)
1262
result = real_bzrdir.open_branch()
2399
1263
# this changes the behaviour of result.clone to create a new reference
2400
1264
# rather than a copy of the content of the branch.
2401
1265
# I did not use a proxy object because that needs much more extensive
2411
network_format_registry = registry.FormatRegistry()
2412
"""Registry of formats indexed by their network name.
2414
The network name for a branch format is an identifier that can be used when
2415
referring to formats with smart server operations. See
2416
BranchFormat.network_name() for more detail.
2420
1275
# formats which have no format string are not discoverable
2421
1276
# and not independently creatable, so are not registered.
2422
1277
__format5 = BzrBranchFormat5()
2423
1278
__format6 = BzrBranchFormat6()
2424
__format7 = BzrBranchFormat7()
2425
__format8 = BzrBranchFormat8()
2426
1279
BranchFormat.register_format(__format5)
2427
1280
BranchFormat.register_format(BranchReferenceFormat())
2428
1281
BranchFormat.register_format(__format6)
2429
BranchFormat.register_format(__format7)
2430
BranchFormat.register_format(__format8)
2431
BranchFormat.set_default_format(__format7)
2432
BranchFormat.register_extra_format(BzrBranchFormat4())
2435
class BranchWriteLockResult(LogicalLockResult):
2436
"""The result of write locking a branch.
2438
:ivar branch_token: The token obtained from the underlying branch lock, or
2440
:ivar unlock: A callable which will unlock the lock.
2443
def __init__(self, unlock, branch_token):
2444
LogicalLockResult.__init__(self, unlock)
2445
self.branch_token = branch_token
2448
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2452
class BzrBranch(Branch, _RelockDebugMixin):
1282
BranchFormat.set_default_format(__format6)
1283
_legacy_formats = [BzrBranchFormat4(),
1286
class BzrBranch(Branch):
2453
1287
"""A branch stored in the actual filesystem.
2455
1289
Note that it's "local" in the context of the filesystem; it doesn't
2456
1290
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2457
1291
it's writable, and can be accessed via the normal filesystem API.
2459
:ivar _transport: Transport for file operations on this branch's
2460
control files, typically pointing to the .bzr/branch directory.
2461
:ivar repository: Repository for this branch.
2462
:ivar base: The url of the base directory for this branch; the one
2463
containing the .bzr directory.
2464
:ivar name: Optional colocated branch name as it exists in the control
2468
1294
def __init__(self, _format=None,
2469
_control_files=None, a_bzrdir=None, name=None,
2470
_repository=None, ignore_fallbacks=False):
1295
_control_files=None, a_bzrdir=None, _repository=None):
2471
1296
"""Create new branch object at a particular location."""
1297
Branch.__init__(self)
2472
1298
if a_bzrdir is None:
2473
1299
raise ValueError('a_bzrdir must be supplied')
2475
1301
self.bzrdir = a_bzrdir
1302
# self._transport used to point to the directory containing the
1303
# control directory, but was not used - now it's just the transport
1304
# for the branch control files. mbp 20070212
2476
1305
self._base = self.bzrdir.transport.clone('..').base
2478
# XXX: We should be able to just do
2479
# self.base = self.bzrdir.root_transport.base
2480
# but this does not quite work yet -- mbp 20080522
2481
1306
self._format = _format
2482
1307
if _control_files is None:
2483
1308
raise ValueError('BzrBranch _control_files is None')
2484
1309
self.control_files = _control_files
2485
1310
self._transport = _control_files._transport
2486
1311
self.repository = _repository
2487
Branch.__init__(self)
2489
1313
def __str__(self):
2490
if self.name is None:
2491
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2493
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
1314
return '%s(%r)' % (self.__class__.__name__, self.base)
2496
1316
__repr__ = __str__
2589
1383
This performs the actual writing to disk.
2590
1384
It is intended to be called by BzrBranch5.set_revision_history."""
2591
self._transport.put_bytes(
2592
'revision-history', '\n'.join(history),
2593
mode=self.bzrdir._get_file_mode())
1385
self.control_files.put_bytes(
1386
'revision-history', '\n'.join(history))
2595
1388
@needs_write_lock
2596
1389
def set_revision_history(self, rev_history):
2597
1390
"""See Branch.set_revision_history."""
2598
1391
if 'evil' in debug.debug_flags:
2599
1392
mutter_callsite(3, "set_revision_history scales with history.")
2600
check_not_reserved_id = _mod_revision.check_not_reserved_id
2601
for rev_id in rev_history:
2602
check_not_reserved_id(rev_id)
2603
if Branch.hooks['post_change_branch_tip']:
2604
# Don't calculate the last_revision_info() if there are no hooks
2606
old_revno, old_revid = self.last_revision_info()
2607
if len(rev_history) == 0:
2608
revid = _mod_revision.NULL_REVISION
2610
revid = rev_history[-1]
2611
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1393
self._clear_cached_state()
2612
1394
self._write_revision_history(rev_history)
2613
self._clear_cached_state()
2614
1395
self._cache_revision_history(rev_history)
2615
1396
for hook in Branch.hooks['set_rh']:
2616
1397
hook(self, rev_history)
2617
if Branch.hooks['post_change_branch_tip']:
2618
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2620
def _synchronize_history(self, destination, revision_id):
2621
"""Synchronize last revision and revision history between branches.
2623
This version is most efficient when the destination is also a
2624
BzrBranch5, but works for BzrBranch6 as long as the revision
2625
history is the true lefthand parent history, and all of the revisions
2626
are in the destination's repository. If not, set_revision_history
2629
:param destination: The branch to copy the history into
2630
:param revision_id: The revision-id to truncate history at. May
2631
be None to copy complete history.
2633
if not isinstance(destination._format, BzrBranchFormat5):
2634
super(BzrBranch, self)._synchronize_history(
2635
destination, revision_id)
2637
if revision_id == _mod_revision.NULL_REVISION:
2640
new_history = self.revision_history()
2641
if revision_id is not None and new_history != []:
2643
new_history = new_history[:new_history.index(revision_id) + 1]
2645
rev = self.repository.get_revision(revision_id)
2646
new_history = rev.get_history(self.repository)[1:]
2647
destination.set_revision_history(new_history)
2649
1399
@needs_write_lock
2650
1400
def set_last_revision_info(self, revno, revision_id):
2688
1456
self.set_revision_history(self._lefthand_history(revision_id,
2689
1457
last_rev, other_branch))
1460
def update_revisions(self, other, stop_revision=None, overwrite=False):
1461
"""See Branch.update_revisions."""
1464
other_last_revno, other_last_revision = other.last_revision_info()
1465
if stop_revision is None:
1466
stop_revision = other_last_revision
1467
if _mod_revision.is_null(stop_revision):
1468
# if there are no commits, we're done.
1470
# whats the current last revision, before we fetch [and change it
1472
last_rev = _mod_revision.ensure_null(self.last_revision())
1473
# we fetch here so that we don't process data twice in the common
1474
# case of having something to pull, and so that the check for
1475
# already merged can operate on the just fetched graph, which will
1476
# be cached in memory.
1477
self.fetch(other, stop_revision)
1478
# Check to see if one is an ancestor of the other
1480
heads = self.repository.get_graph().heads([stop_revision,
1482
if heads == set([last_rev]):
1483
# The current revision is a decendent of the target,
1486
elif heads == set([stop_revision, last_rev]):
1487
# These branches have diverged
1488
raise errors.DivergedBranches(self, other)
1489
assert heads == set([stop_revision])
1490
if other_last_revision == stop_revision:
1491
self.set_last_revision_info(other_last_revno,
1492
other_last_revision)
1494
# TODO: jam 2007-11-29 Is there a way to determine the
1495
# revno without searching all of history??
1497
self.generate_revision_history(stop_revision)
1499
self.generate_revision_history(stop_revision,
1500
last_rev=last_rev, other_branch=other)
2691
1504
def basis_tree(self):
2692
1505
"""See Branch.basis_tree."""
2693
1506
return self.repository.revision_tree(self.last_revision())
1509
def pull(self, source, overwrite=False, stop_revision=None,
1510
_hook_master=None, run_hooks=True, possible_transports=None):
1513
:param _hook_master: Private parameter - set the branch to
1514
be supplied as the master to push hooks.
1515
:param run_hooks: Private parameter - if false, this branch
1516
is being called because it's the master of the primary branch,
1517
so it should not run its hooks.
1519
result = PullResult()
1520
result.source_branch = source
1521
result.target_branch = self
1524
result.old_revno, result.old_revid = self.last_revision_info()
1525
self.update_revisions(source, stop_revision, overwrite=overwrite)
1526
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1527
result.new_revno, result.new_revid = self.last_revision_info()
1529
result.master_branch = _hook_master
1530
result.local_branch = self
1532
result.master_branch = self
1533
result.local_branch = None
1535
for hook in Branch.hooks['post_pull']:
2695
1541
def _get_parent_location(self):
2696
1542
_locs = ['parent', 'pull', 'x-pull']
2697
1543
for l in _locs:
2699
return self._transport.get_bytes(l).strip('\n')
2700
except errors.NoSuchFile:
1545
return self.control_files.get(l).read().strip('\n')
1551
def push(self, target, overwrite=False, stop_revision=None,
1552
_override_hook_source_branch=None):
1555
This is the basic concrete implementation of push()
1557
:param _override_hook_source_branch: If specified, run
1558
the hooks passing this Branch as the source, rather than self.
1559
This is for use of RemoteBranch, where push is delegated to the
1560
underlying vfs-based Branch.
1562
# TODO: Public option to disable running hooks - should be trivial but
1566
result = self._push_with_bound_branches(target, overwrite,
1568
_override_hook_source_branch=_override_hook_source_branch)
1573
def _push_with_bound_branches(self, target, overwrite,
1575
_override_hook_source_branch=None):
1576
"""Push from self into target, and into target's master if any.
1578
This is on the base BzrBranch class even though it doesn't support
1579
bound branches because the *target* might be bound.
1582
if _override_hook_source_branch:
1583
result.source_branch = _override_hook_source_branch
1584
for hook in Branch.hooks['post_push']:
1587
bound_location = target.get_bound_location()
1588
if bound_location and target.base != bound_location:
1589
# there is a master branch.
1591
# XXX: Why the second check? Is it even supported for a branch to
1592
# be bound to itself? -- mbp 20070507
1593
master_branch = target.get_master_branch()
1594
master_branch.lock_write()
1596
# push into the master from this branch.
1597
self._basic_push(master_branch, overwrite, stop_revision)
1598
# and push into the target branch from this. Note that we push from
1599
# this branch again, because its considered the highest bandwidth
1601
result = self._basic_push(target, overwrite, stop_revision)
1602
result.master_branch = master_branch
1603
result.local_branch = target
1607
master_branch.unlock()
1610
result = self._basic_push(target, overwrite, stop_revision)
1611
# TODO: Why set master_branch and local_branch if there's no
1612
# binding? Maybe cleaner to just leave them unset? -- mbp
1614
result.master_branch = target
1615
result.local_branch = None
2704
1619
def _basic_push(self, target, overwrite, stop_revision):
2705
1620
"""Basic implementation of push without bound branches or hooks.
2707
Must be called with source read locked and target write locked.
1622
Must be called with self read locked and target write locked.
2709
result = BranchPushResult()
1624
result = PushResult()
2710
1625
result.source_branch = self
2711
1626
result.target_branch = target
2712
1627
result.old_revno, result.old_revid = target.last_revision_info()
2713
self.update_references(target)
2714
if result.old_revid != stop_revision:
2715
# We assume that during 'push' this repository is closer than
2717
graph = self.repository.get_graph(target.repository)
2718
target.update_revisions(self, stop_revision,
2719
overwrite=overwrite, graph=graph)
2720
if self._push_should_merge_tags():
2721
result.tag_conflicts = self.tags.merge_to(target.tags,
1629
target.update_revisions(self, stop_revision)
1630
except DivergedBranches:
1634
target.set_revision_history(self.revision_history())
1635
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2723
1636
result.new_revno, result.new_revid = target.last_revision_info()
2726
def get_stacked_on_url(self):
2727
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1639
def get_parent(self):
1640
"""See Branch.get_parent."""
1642
assert self.base[-1] == '/'
1643
parent = self._get_parent_location()
1646
# This is an old-format absolute path to a local branch
1647
# turn it into a url
1648
if parent.startswith('/'):
1649
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1651
return urlutils.join(self.base[:-1], parent)
1652
except errors.InvalidURLJoin, e:
1653
raise errors.InaccessibleParent(parent, self.base)
2729
1655
def set_push_location(self, location):
2730
1656
"""See Branch.set_push_location."""
2854
class BzrBranch8(BzrBranch5):
2855
"""A branch that stores tree-reference locations."""
2857
def _open_hook(self):
2858
if self._ignore_fallbacks:
1822
class BzrBranchExperimental(BzrBranch5):
1823
"""Bzr experimental branch format
1826
- a revision-history file.
1828
- a lock dir guarding the branch itself
1829
- all of this stored in a branch/ subdirectory
1830
- works with shared repositories.
1831
- a tag dictionary in the branch
1833
This format is new in bzr 0.15, but shouldn't be used for real data,
1836
This class acts as it's own BranchFormat.
1839
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1842
def get_format_string(cls):
1843
"""See BranchFormat.get_format_string()."""
1844
return "Bazaar-NG branch format experimental\n"
1847
def get_format_description(cls):
1848
"""See BranchFormat.get_format_description()."""
1849
return "Experimental branch format"
1852
def get_reference(cls, a_bzrdir):
1853
"""Get the target reference of the branch in a_bzrdir.
1855
format probing must have been completed before calling
1856
this method - it is assumed that the format of the branch
1857
in a_bzrdir is correct.
1859
:param a_bzrdir: The bzrdir to get the branch data from.
1860
:return: None if the branch is not a reference branch.
1865
def set_reference(self, a_bzrdir, to_branch):
1866
"""Set the target reference of the branch in a_bzrdir.
1868
format probing must have been completed before calling
1869
this method - it is assumed that the format of the branch
1870
in a_bzrdir is correct.
1872
:param a_bzrdir: The bzrdir to set the branch reference for.
1873
:param to_branch: branch that the checkout is to reference
1875
raise NotImplementedError(self.set_reference)
1878
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1880
branch_transport = a_bzrdir.get_branch_transport(cls)
1881
control_files = lockable_files.LockableFiles(branch_transport,
1882
lock_filename, lock_class)
1883
control_files.create_lock()
1884
control_files.lock_write()
2861
url = self.get_stacked_on_url()
2862
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2863
errors.UnstackableBranchFormat):
2866
for hook in Branch.hooks['transform_fallback_location']:
2867
url = hook(self, url)
2869
hook_name = Branch.hooks.get_hook_name(hook)
2870
raise AssertionError(
2871
"'transform_fallback_location' hook %s returned "
2872
"None, not a URL." % hook_name)
2873
self._activate_fallback_location(url)
2875
def __init__(self, *args, **kwargs):
2876
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2877
super(BzrBranch8, self).__init__(*args, **kwargs)
2878
self._last_revision_info_cache = None
2879
self._reference_info = None
2881
def _clear_cached_state(self):
2882
super(BzrBranch8, self)._clear_cached_state()
2883
self._last_revision_info_cache = None
2884
self._reference_info = None
2886
def _last_revision_info(self):
2887
revision_string = self._transport.get_bytes('last-revision')
1886
for filename, content in utf8_files:
1887
control_files.put_utf8(filename, content)
1889
control_files.unlock()
1892
def initialize(cls, a_bzrdir):
1893
"""Create a branch of this format in a_bzrdir."""
1894
utf8_files = [('format', cls.get_format_string()),
1895
('revision-history', ''),
1896
('branch-name', ''),
1899
cls._initialize_control_files(a_bzrdir, utf8_files,
1900
'lock', lockdir.LockDir)
1901
return cls.open(a_bzrdir, _found=True)
1904
def open(cls, a_bzrdir, _found=False):
1905
"""Return the branch object for a_bzrdir
1907
_found is a private parameter, do not use it. It is used to indicate
1908
if format probing has already be done.
1911
format = BranchFormat.find_format(a_bzrdir)
1912
assert format.__class__ == cls
1913
transport = a_bzrdir.get_branch_transport(None)
1914
control_files = lockable_files.LockableFiles(transport, 'lock',
1916
return cls(_format=cls,
1917
_control_files=control_files,
1919
_repository=a_bzrdir.find_repository())
1922
def is_supported(cls):
1925
def _make_tags(self):
1926
return BasicTags(self)
1929
def supports_tags(cls):
1933
BranchFormat.register_format(BzrBranchExperimental)
1936
class BzrBranch6(BzrBranch5):
1939
def last_revision_info(self):
1940
revision_string = self.control_files.get('last-revision').read()
2888
1941
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2889
1942
revision_id = cache_utf8.get_cached_utf8(revision_id)
2890
1943
revno = int(revno)
2891
1944
return revno, revision_id
1946
def last_revision(self):
1947
"""Return last revision id, or None"""
1948
revision_id = self.last_revision_info()[1]
2893
1951
def _write_last_revision_info(self, revno, revision_id):
2894
1952
"""Simply write out the revision id, with no checks.
2968
2010
"""Set the parent branch"""
2969
2011
return self._get_config_location('parent_location')
2972
def _set_all_reference_info(self, info_dict):
2973
"""Replace all reference info stored in a branch.
2975
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2978
writer = rio.RioWriter(s)
2979
for key, (tree_path, branch_location) in info_dict.iteritems():
2980
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2981
branch_location=branch_location)
2982
writer.write_stanza(stanza)
2983
self._transport.put_bytes('references', s.getvalue())
2984
self._reference_info = info_dict
2987
def _get_all_reference_info(self):
2988
"""Return all the reference info stored in a branch.
2990
:return: A dict of {file_id: (tree_path, branch_location)}
2992
if self._reference_info is not None:
2993
return self._reference_info
2994
rio_file = self._transport.get('references')
2996
stanzas = rio.read_stanzas(rio_file)
2997
info_dict = dict((s['file_id'], (s['tree_path'],
2998
s['branch_location'])) for s in stanzas)
3001
self._reference_info = info_dict
3004
def set_reference_info(self, file_id, tree_path, branch_location):
3005
"""Set the branch location to use for a tree reference.
3007
:param file_id: The file-id of the tree reference.
3008
:param tree_path: The path of the tree reference in the tree.
3009
:param branch_location: The location of the branch to retrieve tree
3012
info_dict = self._get_all_reference_info()
3013
info_dict[file_id] = (tree_path, branch_location)
3014
if None in (tree_path, branch_location):
3015
if tree_path is not None:
3016
raise ValueError('tree_path must be None when branch_location'
3018
if branch_location is not None:
3019
raise ValueError('branch_location must be None when tree_path'
3021
del info_dict[file_id]
3022
self._set_all_reference_info(info_dict)
3024
def get_reference_info(self, file_id):
3025
"""Get the tree_path and branch_location for a tree reference.
3027
:return: a tuple of (tree_path, branch_location)
3029
return self._get_all_reference_info().get(file_id, (None, None))
3031
def reference_parent(self, file_id, path, possible_transports=None):
3032
"""Return the parent branch for a tree-reference file_id.
3034
:param file_id: The file_id of the tree reference
3035
:param path: The path of the file_id in the tree
3036
:return: A branch associated with the file_id
3038
branch_location = self.get_reference_info(file_id)[1]
3039
if branch_location is None:
3040
return Branch.reference_parent(self, file_id, path,
3041
possible_transports)
3042
branch_location = urlutils.join(self.user_url, branch_location)
3043
return Branch.open(branch_location,
3044
possible_transports=possible_transports)
3046
2013
def set_push_location(self, location):
3047
2014
"""See Branch.set_push_location."""
3048
2015
self._set_config_location('push_location', location)
3081
2048
"""See Branch.get_old_bound_location"""
3082
2049
return self._get_bound_location(False)
3084
def get_stacked_on_url(self):
3085
# you can always ask for the URL; but you might not be able to use it
3086
# if the repo can't support stacking.
3087
## self._check_stackable_repo()
3088
stacked_url = self._get_config_location('stacked_on_location')
3089
if stacked_url is None:
3090
raise errors.NotStacked(self)
2051
def set_append_revisions_only(self, enabled):
2056
self.get_config().set_user_option('append_revisions_only', value,
3093
2059
def _get_append_revisions_only(self):
3094
return self.get_config(
3095
).get_user_option_as_bool('append_revisions_only')
3098
def generate_revision_history(self, revision_id, last_rev=None,
3100
"""See BzrBranch5.generate_revision_history"""
3101
history = self._lefthand_history(revision_id, last_rev, other_branch)
3102
revno = len(history)
3103
self.set_last_revision_info(revno, revision_id)
3106
def get_rev_id(self, revno, history=None):
3107
"""Find the revision id of the specified revno."""
3109
return _mod_revision.NULL_REVISION
3111
last_revno, last_revision_id = self.last_revision_info()
3112
if revno <= 0 or revno > last_revno:
3113
raise errors.NoSuchRevision(self, revno)
3115
if history is not None:
3116
return history[revno - 1]
3118
index = last_revno - revno
3119
if len(self._partial_revision_history_cache) <= index:
3120
self._extend_partial_history(stop_index=index)
3121
if len(self._partial_revision_history_cache) > index:
3122
return self._partial_revision_history_cache[index]
2060
value = self.get_config().get_user_option('append_revisions_only')
2061
return value == 'True'
2063
def _synchronize_history(self, destination, revision_id):
2064
"""Synchronize last revision and revision history between branches.
2066
This version is most efficient when the destination is also a
2067
BzrBranch6, but works for BzrBranch5, as long as the destination's
2068
repository contains all the lefthand ancestors of the intended
2069
last_revision. If not, set_last_revision_info will fail.
2071
:param destination: The branch to copy the history into
2072
:param revision_id: The revision-id to truncate history at. May
2073
be None to copy complete history.
2075
source_revno, source_revision_id = self.last_revision_info()
2076
if revision_id is None:
2077
revno, revision_id = source_revno, source_revision_id
2078
elif source_revision_id == revision_id:
2079
# we know the revno without needing to walk all of history
2080
revno = source_revno
3124
raise errors.NoSuchRevision(self, revno)
3127
def revision_id_to_revno(self, revision_id):
3128
"""Given a revision id, return its revno"""
3129
if _mod_revision.is_null(revision_id):
3132
index = self._partial_revision_history_cache.index(revision_id)
3134
self._extend_partial_history(stop_revision=revision_id)
3135
index = len(self._partial_revision_history_cache) - 1
3136
if self._partial_revision_history_cache[index] != revision_id:
3137
raise errors.NoSuchRevision(self, revision_id)
3138
return self.revno() - index
3141
class BzrBranch7(BzrBranch8):
3142
"""A branch with support for a fallback repository."""
3144
def set_reference_info(self, file_id, tree_path, branch_location):
3145
Branch.set_reference_info(self, file_id, tree_path, branch_location)
3147
def get_reference_info(self, file_id):
3148
Branch.get_reference_info(self, file_id)
3150
def reference_parent(self, file_id, path, possible_transports=None):
3151
return Branch.reference_parent(self, file_id, path,
3152
possible_transports)
3155
class BzrBranch6(BzrBranch7):
3156
"""See BzrBranchFormat6 for the capabilities of this branch.
3158
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
3162
def get_stacked_on_url(self):
3163
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2082
# To figure out the revno for a random revision, we need to build
2083
# the revision history, and count its length.
2084
# We don't care about the order, just how long it is.
2085
# Alternatively, we could start at the current location, and count
2086
# backwards. But there is no guarantee that we will find it since
2087
# it may be a merged revision.
2088
revno = len(list(self.repository.iter_reverse_revision_history(
2090
destination.set_last_revision_info(revno, revision_id)
2092
def _make_tags(self):
2093
return BasicTags(self)
3166
2096
######################################################################
3184
2114
:ivar new_revno: Revision number after pull.
3185
2115
:ivar old_revid: Tip revision id before pull.
3186
2116
:ivar new_revid: Tip revision id after pull.
3187
:ivar source_branch: Source (local) branch object. (read locked)
3188
:ivar master_branch: Master branch of the target, or the target if no
3190
:ivar local_branch: target branch if there is a Master, else None
3191
:ivar target_branch: Target/destination branch object. (write locked)
3192
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2117
:ivar source_branch: Source (local) branch object.
2118
:ivar master_branch: Master branch of the target, or None.
2119
:ivar target_branch: Target/destination branch object.
3195
@deprecated_method(deprecated_in((2, 3, 0)))
3196
2122
def __int__(self):
3197
"""Return the relative change in revno.
3199
:deprecated: Use `new_revno` and `old_revno` instead.
2123
# DEPRECATED: pull used to return the change in revno
3201
2124
return self.new_revno - self.old_revno
3203
2126
def report(self, to_file):
3205
if self.old_revid == self.new_revid:
3206
to_file.write('No revisions to pull.\n')
3208
to_file.write('Now on revision %d.\n' % self.new_revno)
2127
if self.old_revid == self.new_revid:
2128
to_file.write('No revisions to pull.\n')
2130
to_file.write('Now on revision %d.\n' % self.new_revno)
3209
2131
self._show_tag_conficts(to_file)
3212
class BranchPushResult(_Result):
2134
class PushResult(_Result):
3213
2135
"""Result of a Branch.push operation.
3215
:ivar old_revno: Revision number (eg 10) of the target before push.
3216
:ivar new_revno: Revision number (eg 12) of the target after push.
3217
:ivar old_revid: Tip revision id (eg joe@foo.com-1234234-aoeua34) of target
3219
:ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target
3221
:ivar source_branch: Source branch object that the push was from. This is
3222
read locked, and generally is a local (and thus low latency) branch.
3223
:ivar master_branch: If target is a bound branch, the master branch of
3224
target, or target itself. Always write locked.
3225
:ivar target_branch: The direct Branch where data is being sent (write
3227
:ivar local_branch: If the target is a bound branch this will be the
3228
target, otherwise it will be None.
2137
:ivar old_revno: Revision number before push.
2138
:ivar new_revno: Revision number after push.
2139
:ivar old_revid: Tip revision id before push.
2140
:ivar new_revid: Tip revision id after push.
2141
:ivar source_branch: Source branch object.
2142
:ivar master_branch: Master branch of the target, or None.
2143
:ivar target_branch: Target/destination branch object.
3231
@deprecated_method(deprecated_in((2, 3, 0)))
3232
2146
def __int__(self):
3233
"""Return the relative change in revno.
3235
:deprecated: Use `new_revno` and `old_revno` instead.
2147
# DEPRECATED: push used to return the change in revno
3237
2148
return self.new_revno - self.old_revno
3239
2150
def report(self, to_file):
3240
2151
"""Write a human-readable description of the result."""
3241
2152
if self.old_revid == self.new_revid:
3242
note('No new revisions to push.')
2153
to_file.write('No new revisions to push.\n')
3244
note('Pushed up to revision %d.' % self.new_revno)
2155
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
3245
2156
self._show_tag_conficts(to_file)
3285
2194
new_branch.tags._set_tag_dict({})
3287
2196
# Copying done; now update target format
3288
new_branch._transport.put_bytes('format',
3289
format.get_format_string(),
3290
mode=new_branch.bzrdir._get_file_mode())
2197
new_branch.control_files.put_utf8('format',
2198
format.get_format_string())
3292
2200
# Clean up old files
3293
new_branch._transport.delete('revision-history')
2201
new_branch.control_files._transport.delete('revision-history')
3295
2203
branch.set_parent(None)
3296
except errors.NoSuchFile:
3298
2206
branch.set_bound_location(None)
3301
class Converter6to7(object):
3302
"""Perform an in-place upgrade of format 6 to format 7"""
3304
def convert(self, branch):
3305
format = BzrBranchFormat7()
3306
branch._set_config_location('stacked_on_location', '')
3307
# update target format
3308
branch._transport.put_bytes('format', format.get_format_string())
3311
class Converter7to8(object):
3312
"""Perform an in-place upgrade of format 6 to format 7"""
3314
def convert(self, branch):
3315
format = BzrBranchFormat8()
3316
branch._transport.put_bytes('references', '')
3317
# update target format
3318
branch._transport.put_bytes('format', format.get_format_string())
3321
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3322
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3325
_run_with_write_locked_target will attempt to release the lock it acquires.
3327
If an exception is raised by callable, then that exception *will* be
3328
propagated, even if the unlock attempt raises its own error. Thus
3329
_run_with_write_locked_target should be preferred to simply doing::
3333
return callable(*args, **kwargs)
3338
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3339
# should share code?
3342
result = callable(*args, **kwargs)
3344
exc_info = sys.exc_info()
3348
raise exc_info[0], exc_info[1], exc_info[2]
3354
class InterBranch(InterObject):
3355
"""This class represents operations taking place between two branches.
3357
Its instances have methods like pull() and push() and contain
3358
references to the source and target repositories these operations
3359
can be carried out on.
3363
"""The available optimised InterBranch types."""
3366
def _get_branch_formats_to_test(klass):
3367
"""Return an iterable of format tuples for testing.
3369
:return: An iterable of (from_format, to_format) to use when testing
3370
this InterBranch class. Each InterBranch class should define this
3373
raise NotImplementedError(klass._get_branch_formats_to_test)
3376
def pull(self, overwrite=False, stop_revision=None,
3377
possible_transports=None, local=False):
3378
"""Mirror source into target branch.
3380
The target branch is considered to be 'local', having low latency.
3382
:returns: PullResult instance
3384
raise NotImplementedError(self.pull)
3387
def update_revisions(self, stop_revision=None, overwrite=False,
3388
graph=None, fetch_tags=True):
3389
"""Pull in new perfect-fit revisions.
3391
:param stop_revision: Updated until the given revision
3392
:param overwrite: Always set the branch pointer, rather than checking
3393
to see if it is a proper descendant.
3394
:param graph: A Graph object that can be used to query history
3395
information. This can be None.
3396
:param fetch_tags: Flag that specifies if tags from source should be
3400
raise NotImplementedError(self.update_revisions)
3403
def push(self, overwrite=False, stop_revision=None,
3404
_override_hook_source_branch=None):
3405
"""Mirror the source branch into the target branch.
3407
The source branch is considered to be 'local', having low latency.
3409
raise NotImplementedError(self.push)
3412
def copy_content_into(self, revision_id=None):
3413
"""Copy the content of source into target
3415
revision_id: if not None, the revision history in the new branch will
3416
be truncated to end with revision_id.
3418
raise NotImplementedError(self.copy_content_into)
3421
class GenericInterBranch(InterBranch):
3422
"""InterBranch implementation that uses public Branch functions."""
3425
def is_compatible(klass, source, target):
3426
# GenericBranch uses the public API, so always compatible
3430
def _get_branch_formats_to_test(klass):
3431
return [(BranchFormat._default_format, BranchFormat._default_format)]
3434
def unwrap_format(klass, format):
3435
if isinstance(format, remote.RemoteBranchFormat):
3436
format._ensure_real()
3437
return format._custom_format
3441
def copy_content_into(self, revision_id=None):
3442
"""Copy the content of source into target
3444
revision_id: if not None, the revision history in the new branch will
3445
be truncated to end with revision_id.
3447
self.source.update_references(self.target)
3448
self.source._synchronize_history(self.target, revision_id)
3450
parent = self.source.get_parent()
3451
except errors.InaccessibleParent, e:
3452
mutter('parent was not accessible to copy: %s', e)
3455
self.target.set_parent(parent)
3456
if self.source._push_should_merge_tags():
3457
self.source.tags.merge_to(self.target.tags)
3460
def update_revisions(self, stop_revision=None, overwrite=False,
3461
graph=None, fetch_tags=True):
3462
"""See InterBranch.update_revisions()."""
3463
other_revno, other_last_revision = self.source.last_revision_info()
3464
stop_revno = None # unknown
3465
if stop_revision is None:
3466
stop_revision = other_last_revision
3467
if _mod_revision.is_null(stop_revision):
3468
# if there are no commits, we're done.
3470
stop_revno = other_revno
3472
# what's the current last revision, before we fetch [and change it
3474
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3475
# we fetch here so that we don't process data twice in the common
3476
# case of having something to pull, and so that the check for
3477
# already merged can operate on the just fetched graph, which will
3478
# be cached in memory.
3480
fetch_spec_factory = fetch.FetchSpecFactory()
3481
fetch_spec_factory.source_branch = self.source
3482
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3483
fetch_spec_factory.source_repo = self.source.repository
3484
fetch_spec_factory.target_repo = self.target.repository
3485
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3486
fetch_spec = fetch_spec_factory.make_fetch_spec()
3488
fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
3489
self.source.repository, revision_ids=[stop_revision]).execute()
3490
self.target.fetch(self.source, fetch_spec=fetch_spec)
3491
# Check to see if one is an ancestor of the other
3494
graph = self.target.repository.get_graph()
3495
if self.target._check_if_descendant_or_diverged(
3496
stop_revision, last_rev, graph, self.source):
3497
# stop_revision is a descendant of last_rev, but we aren't
3498
# overwriting, so we're done.
3500
if stop_revno is None:
3502
graph = self.target.repository.get_graph()
3503
this_revno, this_last_revision = \
3504
self.target.last_revision_info()
3505
stop_revno = graph.find_distance_to_null(stop_revision,
3506
[(other_last_revision, other_revno),
3507
(this_last_revision, this_revno)])
3508
self.target.set_last_revision_info(stop_revno, stop_revision)
3511
def pull(self, overwrite=False, stop_revision=None,
3512
possible_transports=None, run_hooks=True,
3513
_override_hook_target=None, local=False):
3514
"""Pull from source into self, updating my master if any.
3516
:param run_hooks: Private parameter - if false, this branch
3517
is being called because it's the master of the primary branch,
3518
so it should not run its hooks.
3520
bound_location = self.target.get_bound_location()
3521
if local and not bound_location:
3522
raise errors.LocalRequiresBoundBranch()
3523
master_branch = None
3524
source_is_master = (self.source.user_url == bound_location)
3525
if not local and bound_location and not source_is_master:
3526
# not pulling from master, so we need to update master.
3527
master_branch = self.target.get_master_branch(possible_transports)
3528
master_branch.lock_write()
3531
# pull from source into master.
3532
master_branch.pull(self.source, overwrite, stop_revision,
3534
return self._pull(overwrite,
3535
stop_revision, _hook_master=master_branch,
3536
run_hooks=run_hooks,
3537
_override_hook_target=_override_hook_target,
3538
merge_tags_to_master=not source_is_master)
3541
master_branch.unlock()
3543
def push(self, overwrite=False, stop_revision=None,
3544
_override_hook_source_branch=None):
3545
"""See InterBranch.push.
3547
This is the basic concrete implementation of push()
3549
:param _override_hook_source_branch: If specified, run
3550
the hooks passing this Branch as the source, rather than self.
3551
This is for use of RemoteBranch, where push is delegated to the
3552
underlying vfs-based Branch.
3554
# TODO: Public option to disable running hooks - should be trivial but
3556
self.source.lock_read()
3558
return _run_with_write_locked_target(
3559
self.target, self._push_with_bound_branches, overwrite,
3561
_override_hook_source_branch=_override_hook_source_branch)
3563
self.source.unlock()
3565
def _push_with_bound_branches(self, overwrite, stop_revision,
3566
_override_hook_source_branch=None):
3567
"""Push from source into target, and into target's master if any.
3570
if _override_hook_source_branch:
3571
result.source_branch = _override_hook_source_branch
3572
for hook in Branch.hooks['post_push']:
3575
bound_location = self.target.get_bound_location()
3576
if bound_location and self.target.base != bound_location:
3577
# there is a master branch.
3579
# XXX: Why the second check? Is it even supported for a branch to
3580
# be bound to itself? -- mbp 20070507
3581
master_branch = self.target.get_master_branch()
3582
master_branch.lock_write()
3584
# push into the master from the source branch.
3585
self.source._basic_push(master_branch, overwrite, stop_revision)
3586
# and push into the target branch from the source. Note that we
3587
# push from the source branch again, because it's considered the
3588
# highest bandwidth repository.
3589
result = self.source._basic_push(self.target, overwrite,
3591
result.master_branch = master_branch
3592
result.local_branch = self.target
3596
master_branch.unlock()
3599
result = self.source._basic_push(self.target, overwrite,
3601
# TODO: Why set master_branch and local_branch if there's no
3602
# binding? Maybe cleaner to just leave them unset? -- mbp
3604
result.master_branch = self.target
3605
result.local_branch = None
3609
def _pull(self, overwrite=False, stop_revision=None,
3610
possible_transports=None, _hook_master=None, run_hooks=True,
3611
_override_hook_target=None, local=False,
3612
merge_tags_to_master=True):
3615
This function is the core worker, used by GenericInterBranch.pull to
3616
avoid duplication when pulling source->master and source->local.
3618
:param _hook_master: Private parameter - set the branch to
3619
be supplied as the master to pull hooks.
3620
:param run_hooks: Private parameter - if false, this branch
3621
is being called because it's the master of the primary branch,
3622
so it should not run its hooks.
3623
:param _override_hook_target: Private parameter - set the branch to be
3624
supplied as the target_branch to pull hooks.
3625
:param local: Only update the local branch, and not the bound branch.
3627
# This type of branch can't be bound.
3629
raise errors.LocalRequiresBoundBranch()
3630
result = PullResult()
3631
result.source_branch = self.source
3632
if _override_hook_target is None:
3633
result.target_branch = self.target
3635
result.target_branch = _override_hook_target
3636
self.source.lock_read()
3638
# We assume that during 'pull' the target repository is closer than
3640
self.source.update_references(self.target)
3641
graph = self.target.repository.get_graph(self.source.repository)
3642
# TODO: Branch formats should have a flag that indicates
3643
# that revno's are expensive, and pull() should honor that flag.
3645
result.old_revno, result.old_revid = \
3646
self.target.last_revision_info()
3647
self.target.update_revisions(self.source, stop_revision,
3648
overwrite=overwrite, graph=graph)
3649
# TODO: The old revid should be specified when merging tags,
3650
# so a tags implementation that versions tags can only
3651
# pull in the most recent changes. -- JRV20090506
3652
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3653
overwrite, ignore_master=not merge_tags_to_master)
3654
result.new_revno, result.new_revid = self.target.last_revision_info()
3656
result.master_branch = _hook_master
3657
result.local_branch = result.target_branch
3659
result.master_branch = result.target_branch
3660
result.local_branch = None
3662
for hook in Branch.hooks['post_pull']:
3665
self.source.unlock()
3669
InterBranch.register_optimiser(GenericInterBranch)