760
805
if not self._format.supports_stacking():
761
raise errors.UnstackableBranchFormat(self._format, self.user_url)
762
# XXX: Changing from one fallback repository to another does not check
763
# that all the data you need is present in the new fallback.
764
# Possibly it should.
765
self._check_stackable_repo()
768
old_url = self.get_stacked_on_url()
769
except (errors.NotStacked, errors.UnstackableBranchFormat,
770
errors.UnstackableRepositoryFormat):
774
self._activate_fallback_location(url)
775
# write this out after the repository is stacked to avoid setting a
776
# stacked config that doesn't work.
777
self._set_config_location('stacked_on_location', url)
806
raise UnstackableBranchFormat(self._format, self.user_url)
807
with self.lock_write():
808
# XXX: Changing from one fallback repository to another does not
809
# check that all the data you need is present in the new fallback.
810
# Possibly it should.
811
self._check_stackable_repo()
814
self.get_stacked_on_url()
815
except (errors.NotStacked, UnstackableBranchFormat,
816
errors.UnstackableRepositoryFormat):
820
self._activate_fallback_location(
821
url, possible_transports=[self.controldir.root_transport])
822
# write this out after the repository is stacked to avoid setting a
823
# stacked config that doesn't work.
824
self._set_config_location('stacked_on_location', url)
779
826
def _unstack(self):
780
827
"""Change a branch to be unstacked, copying data as needed.
782
829
Don't call this directly, use set_stacked_on_url(None).
784
pb = ui.ui_factory.nested_progress_bar()
786
pb.update("Unstacking")
831
with ui.ui_factory.nested_progress_bar() as pb:
832
pb.update(gettext("Unstacking"))
787
833
# The basic approach here is to fetch the tip of the branch,
788
834
# including all available ghosts, from the existing stacked
789
# repository into a new repository object without the fallbacks.
835
# repository into a new repository object without the fallbacks.
791
837
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
792
838
# correct for CHKMap repostiories
793
839
old_repository = self.repository
794
840
if len(old_repository._fallback_repositories) != 1:
795
raise AssertionError("can't cope with fallback repositories "
796
"of %r" % (self.repository,))
797
# unlock it, including unlocking the fallback
841
raise AssertionError(
842
"can't cope with fallback repositories "
843
"of %r (fallbacks: %r)" % (
844
old_repository, old_repository._fallback_repositories))
845
# Open the new repository object.
846
# Repositories don't offer an interface to remove fallback
847
# repositories today; take the conceptually simpler option and just
848
# reopen it. We reopen it starting from the URL so that we
849
# get a separate connection for RemoteRepositories and can
850
# stream from one of them to the other. This does mean doing
851
# separate SSH connection setup, but unstacking is not a
852
# common operation so it's tolerable.
853
new_bzrdir = controldir.ControlDir.open(
854
self.controldir.root_transport.base)
855
new_repository = new_bzrdir.find_repository()
856
if new_repository._fallback_repositories:
857
raise AssertionError(
858
"didn't expect %r to have fallback_repositories"
859
% (self.repository,))
860
# Replace self.repository with the new repository.
861
# Do our best to transfer the lock state (i.e. lock-tokens and
862
# lock count) of self.repository to the new repository.
863
lock_token = old_repository.lock_write().repository_token
864
self.repository = new_repository
865
if isinstance(self, remote.RemoteBranch):
866
# Remote branches can have a second reference to the old
867
# repository that need to be replaced.
868
if self._real_branch is not None:
869
self._real_branch.repository = new_repository
870
self.repository.lock_write(token=lock_token)
871
if lock_token is not None:
872
old_repository.leave_lock_in_place()
798
873
old_repository.unlock()
799
old_repository.lock_read()
801
# Repositories don't offer an interface to remove fallback
802
# repositories today; take the conceptually simpler option and just
803
# reopen it. We reopen it starting from the URL so that we
804
# get a separate connection for RemoteRepositories and can
805
# stream from one of them to the other. This does mean doing
806
# separate SSH connection setup, but unstacking is not a
807
# common operation so it's tolerable.
808
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
809
new_repository = new_bzrdir.find_repository()
810
self.repository = new_repository
811
if self.repository._fallback_repositories:
812
raise AssertionError("didn't expect %r to have "
813
"fallback_repositories"
814
% (self.repository,))
815
# this is not paired with an unlock because it's just restoring
816
# the previous state; the lock's released when set_stacked_on_url
874
if lock_token is not None:
875
# XXX: self.repository.leave_lock_in_place() before this
876
# function will not be preserved. Fortunately that doesn't
877
# affect the current default format (2a), and would be a
878
# corner-case anyway.
879
# - Andrew Bennetts, 2010/06/30
880
self.repository.dont_leave_lock_in_place()
884
old_repository.unlock()
885
except errors.LockNotHeld:
888
if old_lock_count == 0:
889
raise AssertionError(
890
'old_repository should have been locked at least once.')
891
for i in range(old_lock_count - 1):
818
892
self.repository.lock_write()
893
# Fetch from the old repository into the new.
894
with old_repository.lock_read():
819
895
# XXX: If you unstack a branch while it has a working tree
820
896
# with a pending merge, the pending-merged revisions will no
821
897
# longer be present. You can (probably) revert and remerge.
823
# XXX: This only fetches up to the tip of the repository; it
824
# doesn't bring across any tags. That's fairly consistent
825
# with how branch works, but perhaps not ideal.
826
self.repository.fetch(old_repository,
827
revision_id=self.last_revision(),
830
old_repository.unlock()
834
def _set_tags_bytes(self, bytes):
835
"""Mirror method for _get_tags_bytes.
837
:seealso: Branch._get_tags_bytes.
839
return _run_with_write_locked_target(self, self._transport.put_bytes,
899
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
900
except errors.TagsNotSupported:
901
tags_to_fetch = set()
902
fetch_spec = vf_search.NotInOtherForRevs(
903
self.repository, old_repository,
904
required_ids=[self.last_revision()],
905
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
906
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
842
908
def _cache_revision_history(self, rev_history):
843
909
"""Set the cached revision history to rev_history.
921
982
"""Return last revision id, or NULL_REVISION."""
922
983
return self.last_revision_info()[1]
925
985
def last_revision_info(self):
926
986
"""Return information about the last revision.
928
988
:return: A tuple (revno, revision_id).
930
if self._last_revision_info_cache is None:
931
self._last_revision_info_cache = self._last_revision_info()
932
return self._last_revision_info_cache
934
def _last_revision_info(self):
935
rh = self.revision_history()
938
return (revno, rh[-1])
940
return (0, _mod_revision.NULL_REVISION)
942
@deprecated_method(deprecated_in((1, 6, 0)))
943
def missing_revisions(self, other, stop_revision=None):
944
"""Return a list of new revisions that would perfectly fit.
946
If self and other have not diverged, return a list of the revisions
947
present in other, but missing from self.
949
self_history = self.revision_history()
950
self_len = len(self_history)
951
other_history = other.revision_history()
952
other_len = len(other_history)
953
common_index = min(self_len, other_len) -1
954
if common_index >= 0 and \
955
self_history[common_index] != other_history[common_index]:
956
raise errors.DivergedBranches(self, other)
958
if stop_revision is None:
959
stop_revision = other_len
961
if stop_revision > other_len:
962
raise errors.NoSuchRevision(self, stop_revision)
963
return other_history[self_len:stop_revision]
966
def update_revisions(self, other, stop_revision=None, overwrite=False,
968
"""Pull in new perfect-fit revisions.
970
:param other: Another Branch to pull from
971
:param stop_revision: Updated until the given revision
972
:param overwrite: Always set the branch pointer, rather than checking
973
to see if it is a proper descendant.
974
:param graph: A Graph object that can be used to query history
975
information. This can be None.
978
return InterBranch.get(other, self).update_revisions(stop_revision,
981
def import_last_revision_info(self, source_repo, revno, revid):
990
with self.lock_read():
991
if self._last_revision_info_cache is None:
992
self._last_revision_info_cache = (
993
self._read_last_revision_info())
994
return self._last_revision_info_cache
996
def _read_last_revision_info(self):
997
raise NotImplementedError(self._read_last_revision_info)
999
def import_last_revision_info_and_tags(self, source, revno, revid,
982
1001
"""Set the last revision info, importing from another repo if necessary.
984
1003
This is used by the bound branch code to upload a revision to
985
1004
the master branch first before updating the tip of the local branch.
1005
Revisions referenced by source's tags are also transferred.
987
:param source_repo: Source repository to optionally fetch from
1007
:param source: Source branch to optionally fetch from
988
1008
:param revno: Revision number of the new tip
989
1009
:param revid: Revision id of the new tip
1010
:param lossy: Whether to discard metadata that can not be
1011
natively represented
1012
:return: Tuple with the new revision number and revision id
1013
(should only be different from the arguments when lossy=True)
991
if not self.repository.has_same_location(source_repo):
992
self.repository.fetch(source_repo, revision_id=revid)
1015
if not self.repository.has_same_location(source.repository):
1016
self.fetch(source, revid)
993
1017
self.set_last_revision_info(revno, revid)
1018
return (revno, revid)
995
1020
def revision_id_to_revno(self, revision_id):
996
1021
"""Given a revision id, return its revno"""
997
1022
if _mod_revision.is_null(revision_id):
999
history = self.revision_history()
1024
history = self._revision_history()
1001
1026
return history.index(revision_id) + 1
1002
1027
except ValueError:
1003
1028
raise errors.NoSuchRevision(self, revision_id)
1006
1030
def get_rev_id(self, revno, history=None):
1007
1031
"""Find the revision id of the specified revno."""
1009
return _mod_revision.NULL_REVISION
1010
last_revno, last_revid = self.last_revision_info()
1011
if revno == last_revno:
1013
if revno <= 0 or revno > last_revno:
1014
raise errors.NoSuchRevision(self, revno)
1015
distance_from_last = last_revno - revno
1016
if len(self._partial_revision_history_cache) <= distance_from_last:
1017
self._extend_partial_history(distance_from_last)
1018
return self._partial_revision_history_cache[distance_from_last]
1032
with self.lock_read():
1034
return _mod_revision.NULL_REVISION
1035
last_revno, last_revid = self.last_revision_info()
1036
if revno == last_revno:
1038
if revno <= 0 or revno > last_revno:
1039
raise errors.NoSuchRevision(self, revno)
1040
distance_from_last = last_revno - revno
1041
if len(self._partial_revision_history_cache) <= distance_from_last:
1042
self._extend_partial_history(distance_from_last)
1043
return self._partial_revision_history_cache[distance_from_last]
1021
1045
def pull(self, source, overwrite=False, stop_revision=None,
1022
1046
possible_transports=None, *args, **kwargs):
1023
1047
"""Mirror source into this branch.
1910
1869
return self.__dict__ == other.__dict__
1912
1871
def __repr__(self):
1913
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1914
self.control_dir, self.to_branch,
1872
return "<%s for %s to (%s, %s)>" % (
1873
self.__class__.__name__, self.control_dir, self.to_branch,
1915
1874
self.revision_id)
1918
class BzrBranchFormat4(BranchFormat):
1919
"""Bzr branch format 4.
1922
- a revision-history file.
1923
- a branch-lock lock file [ to be shared with the bzrdir ]
1926
def get_format_description(self):
1927
"""See BranchFormat.get_format_description()."""
1928
return "Branch format 4"
1930
def initialize(self, a_bzrdir, name=None):
1931
"""Create a branch of this format in a_bzrdir."""
1932
utf8_files = [('revision-history', ''),
1933
('branch-name', ''),
1935
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1936
lock_type='branch4', set_format=False)
1939
super(BzrBranchFormat4, self).__init__()
1940
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1942
def network_name(self):
1943
"""The network name for this format is the control dirs disk label."""
1944
return self._matchingbzrdir.get_format_string()
1946
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1947
"""See BranchFormat.open()."""
1949
# we are being called directly and must probe.
1950
raise NotImplementedError
1951
return BzrBranch(_format=self,
1952
_control_files=a_bzrdir._control_files,
1955
_repository=a_bzrdir.open_repository())
1958
return "Bazaar-NG branch format 4"
1961
class BranchFormatMetadir(BranchFormat):
1962
"""Common logic for meta-dir based branch formats."""
1964
def _branch_class(self):
1965
"""What class to instantiate on open calls."""
1966
raise NotImplementedError(self._branch_class)
1968
def network_name(self):
1969
"""A simple byte string uniquely identifying this format for RPC calls.
1971
Metadir branch formats use their format string.
1973
return self.get_format_string()
1975
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1976
"""See BranchFormat.open()."""
1978
format = BranchFormat.find_format(a_bzrdir, name=name)
1979
if format.__class__ != self.__class__:
1980
raise AssertionError("wrong format %r found for %r" %
1982
transport = a_bzrdir.get_branch_transport(None, name=name)
1984
control_files = lockable_files.LockableFiles(transport, 'lock',
1986
return self._branch_class()(_format=self,
1987
_control_files=control_files,
1990
_repository=a_bzrdir.find_repository(),
1991
ignore_fallbacks=ignore_fallbacks)
1992
except errors.NoSuchFile:
1993
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1996
super(BranchFormatMetadir, self).__init__()
1997
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1998
self._matchingbzrdir.set_branch_format(self)
2000
def supports_tags(self):
2004
class BzrBranchFormat5(BranchFormatMetadir):
2005
"""Bzr branch format 5.
2008
- a revision-history file.
2010
- a lock dir guarding the branch itself
2011
- all of this stored in a branch/ subdirectory
2012
- works with shared repositories.
2014
This format is new in bzr 0.8.
2017
def _branch_class(self):
2020
def get_format_string(self):
2021
"""See BranchFormat.get_format_string()."""
2022
return "Bazaar-NG branch format 5\n"
2024
def get_format_description(self):
2025
"""See BranchFormat.get_format_description()."""
2026
return "Branch format 5"
2028
def initialize(self, a_bzrdir, name=None):
2029
"""Create a branch of this format in a_bzrdir."""
2030
utf8_files = [('revision-history', ''),
2031
('branch-name', ''),
2033
return self._initialize_helper(a_bzrdir, utf8_files, name)
2035
def supports_tags(self):
2039
class BzrBranchFormat6(BranchFormatMetadir):
2040
"""Branch format with last-revision and tags.
2042
Unlike previous formats, this has no explicit revision history. Instead,
2043
this just stores the last-revision, and the left-hand history leading
2044
up to there is the history.
2046
This format was introduced in bzr 0.15
2047
and became the default in 0.91.
2050
def _branch_class(self):
2053
def get_format_string(self):
2054
"""See BranchFormat.get_format_string()."""
2055
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2057
def get_format_description(self):
2058
"""See BranchFormat.get_format_description()."""
2059
return "Branch format 6"
2061
def initialize(self, a_bzrdir, name=None):
2062
"""Create a branch of this format in a_bzrdir."""
2063
utf8_files = [('last-revision', '0 null:\n'),
2064
('branch.conf', ''),
2067
return self._initialize_helper(a_bzrdir, utf8_files, name)
2069
def make_tags(self, branch):
2070
"""See bzrlib.branch.BranchFormat.make_tags()."""
2071
return BasicTags(branch)
2073
def supports_set_append_revisions_only(self):
2077
class BzrBranchFormat8(BranchFormatMetadir):
2078
"""Metadir format supporting storing locations of subtree branches."""
2080
def _branch_class(self):
2083
def get_format_string(self):
2084
"""See BranchFormat.get_format_string()."""
2085
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2087
def get_format_description(self):
2088
"""See BranchFormat.get_format_description()."""
2089
return "Branch format 8"
2091
def initialize(self, a_bzrdir, name=None):
2092
"""Create a branch of this format in a_bzrdir."""
2093
utf8_files = [('last-revision', '0 null:\n'),
2094
('branch.conf', ''),
2098
return self._initialize_helper(a_bzrdir, utf8_files, name)
2101
super(BzrBranchFormat8, self).__init__()
2102
self._matchingbzrdir.repository_format = \
2103
RepositoryFormatKnitPack5RichRoot()
2105
def make_tags(self, branch):
2106
"""See bzrlib.branch.BranchFormat.make_tags()."""
2107
return BasicTags(branch)
2109
def supports_set_append_revisions_only(self):
2112
def supports_stacking(self):
2115
supports_reference_locations = True
2118
class BzrBranchFormat7(BzrBranchFormat8):
2119
"""Branch format with last-revision, tags, and a stacked location pointer.
2121
The stacked location pointer is passed down to the repository and requires
2122
a repository format with supports_external_lookups = True.
2124
This format was introduced in bzr 1.6.
2127
def initialize(self, a_bzrdir, name=None):
2128
"""Create a branch of this format in a_bzrdir."""
2129
utf8_files = [('last-revision', '0 null:\n'),
2130
('branch.conf', ''),
2133
return self._initialize_helper(a_bzrdir, utf8_files, name)
2135
def _branch_class(self):
2138
def get_format_string(self):
2139
"""See BranchFormat.get_format_string()."""
2140
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2142
def get_format_description(self):
2143
"""See BranchFormat.get_format_description()."""
2144
return "Branch format 7"
2146
def supports_set_append_revisions_only(self):
2149
supports_reference_locations = False
2152
class BranchReferenceFormat(BranchFormat):
2153
"""Bzr branch reference format.
2155
Branch references are used in implementing checkouts, they
2156
act as an alias to the real branch which is at some other url.
2163
def get_format_string(self):
2164
"""See BranchFormat.get_format_string()."""
2165
return "Bazaar-NG Branch Reference Format 1\n"
2167
def get_format_description(self):
2168
"""See BranchFormat.get_format_description()."""
2169
return "Checkout reference format 1"
2171
def get_reference(self, a_bzrdir):
2172
"""See BranchFormat.get_reference()."""
2173
transport = a_bzrdir.get_branch_transport(None)
2174
return transport.get_bytes('location')
2176
def set_reference(self, a_bzrdir, to_branch):
2177
"""See BranchFormat.set_reference()."""
2178
transport = a_bzrdir.get_branch_transport(None)
2179
location = transport.put_bytes('location', to_branch.base)
2181
def initialize(self, a_bzrdir, name=None, target_branch=None):
2182
"""Create a branch of this format in a_bzrdir."""
2183
if target_branch is None:
2184
# this format does not implement branch itself, thus the implicit
2185
# creation contract must see it as uninitializable
2186
raise errors.UninitializableFormat(self)
2187
mutter('creating branch reference in %s', a_bzrdir.user_url)
2188
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2189
branch_transport.put_bytes('location',
2190
target_branch.bzrdir.user_url)
2191
branch_transport.put_bytes('format', self.get_format_string())
2193
a_bzrdir, name, _found=True,
2194
possible_transports=[target_branch.bzrdir.root_transport])
2195
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2199
super(BranchReferenceFormat, self).__init__()
2200
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2201
self._matchingbzrdir.set_branch_format(self)
2203
def _make_reference_clone_function(format, a_branch):
2204
"""Create a clone() routine for a branch dynamically."""
2205
def clone(to_bzrdir, revision_id=None,
2206
repository_policy=None):
2207
"""See Branch.clone()."""
2208
return format.initialize(to_bzrdir, target_branch=a_branch)
2209
# cannot obey revision_id limits when cloning a reference ...
2210
# FIXME RBC 20060210 either nuke revision_id for clone, or
2211
# emit some sort of warning/error to the caller ?!
2214
def open(self, a_bzrdir, name=None, _found=False, location=None,
2215
possible_transports=None, ignore_fallbacks=False):
2216
"""Return the branch that the branch reference in a_bzrdir points at.
2218
:param a_bzrdir: A BzrDir that contains a branch.
2219
:param name: Name of colocated branch to open, if any
2220
:param _found: a private parameter, do not use it. It is used to
2221
indicate if format probing has already be done.
2222
:param ignore_fallbacks: when set, no fallback branches will be opened
2223
(if there are any). Default is to open fallbacks.
2224
:param location: The location of the referenced branch. If
2225
unspecified, this will be determined from the branch reference in
2227
:param possible_transports: An optional reusable transports list.
2230
format = BranchFormat.find_format(a_bzrdir, name=name)
2231
if format.__class__ != self.__class__:
2232
raise AssertionError("wrong format %r found for %r" %
2234
if location is None:
2235
location = self.get_reference(a_bzrdir)
2236
real_bzrdir = bzrdir.BzrDir.open(
2237
location, possible_transports=possible_transports)
2238
result = real_bzrdir.open_branch(name=name,
2239
ignore_fallbacks=ignore_fallbacks)
2240
# this changes the behaviour of result.clone to create a new reference
2241
# rather than a copy of the content of the branch.
2242
# I did not use a proxy object because that needs much more extensive
2243
# testing, and we are only changing one behaviour at the moment.
2244
# If we decide to alter more behaviours - i.e. the implicit nickname
2245
# then this should be refactored to introduce a tested proxy branch
2246
# and a subclass of that for use in overriding clone() and ....
2248
result.clone = self._make_reference_clone_function(result)
1877
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1878
"""Branch format registry."""
1880
def __init__(self, other_registry=None):
1881
super(BranchFormatRegistry, self).__init__(other_registry)
1882
self._default_format = None
1883
self._default_format_key = None
1885
def get_default(self):
1886
"""Return the current default format."""
1887
if (self._default_format_key is not None
1888
and self._default_format is None):
1889
self._default_format = self.get(self._default_format_key)
1890
return self._default_format
1892
def set_default(self, format):
1893
"""Set the default format."""
1894
self._default_format = format
1895
self._default_format_key = None
1897
def set_default_key(self, format_string):
1898
"""Set the default format by its format string."""
1899
self._default_format_key = format_string
1900
self._default_format = None
2252
1903
network_format_registry = registry.FormatRegistry()
2257
1908
BranchFormat.network_name() for more detail.
1911
format_registry = BranchFormatRegistry(network_format_registry)
2261
1914
# formats which have no format string are not discoverable
2262
1915
# and not independently creatable, so are not registered.
2263
__format5 = BzrBranchFormat5()
2264
__format6 = BzrBranchFormat6()
2265
__format7 = BzrBranchFormat7()
2266
__format8 = BzrBranchFormat8()
2267
BranchFormat.register_format(__format5)
2268
BranchFormat.register_format(BranchReferenceFormat())
2269
BranchFormat.register_format(__format6)
2270
BranchFormat.register_format(__format7)
2271
BranchFormat.register_format(__format8)
2272
BranchFormat.set_default_format(__format7)
2273
_legacy_formats = [BzrBranchFormat4(),
2275
network_format_registry.register(
2276
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2279
class BranchWriteLockResult(object):
1916
format_registry.register_lazy(
1917
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1919
format_registry.register_lazy(
1920
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1921
"breezy.bzr.branch", "BzrBranchFormat6")
1922
format_registry.register_lazy(
1923
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1924
"breezy.bzr.branch", "BzrBranchFormat7")
1925
format_registry.register_lazy(
1926
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1927
"breezy.bzr.branch", "BzrBranchFormat8")
1928
format_registry.register_lazy(
1929
b"Bazaar-NG Branch Reference Format 1\n",
1930
"breezy.bzr.branch", "BranchReferenceFormat")
1932
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1935
class BranchWriteLockResult(LogicalLockResult):
2280
1936
"""The result of write locking a branch.
2282
:ivar branch_token: The token obtained from the underlying branch lock, or
1938
:ivar token: The token obtained from the underlying branch lock, or
2284
1940
:ivar unlock: A callable which will unlock the lock.
2287
def __init__(self, unlock, branch_token):
2288
self.branch_token = branch_token
2289
self.unlock = unlock
2292
class BzrBranch(Branch, _RelockDebugMixin):
2293
"""A branch stored in the actual filesystem.
2295
Note that it's "local" in the context of the filesystem; it doesn't
2296
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2297
it's writable, and can be accessed via the normal filesystem API.
2299
:ivar _transport: Transport for file operations on this branch's
2300
control files, typically pointing to the .bzr/branch directory.
2301
:ivar repository: Repository for this branch.
2302
:ivar base: The url of the base directory for this branch; the one
2303
containing the .bzr directory.
2304
:ivar name: Optional colocated branch name as it exists in the control
2308
def __init__(self, _format=None,
2309
_control_files=None, a_bzrdir=None, name=None,
2310
_repository=None, ignore_fallbacks=False):
2311
"""Create new branch object at a particular location."""
2312
if a_bzrdir is None:
2313
raise ValueError('a_bzrdir must be supplied')
2315
self.bzrdir = a_bzrdir
2316
self._base = self.bzrdir.transport.clone('..').base
2318
# XXX: We should be able to just do
2319
# self.base = self.bzrdir.root_transport.base
2320
# but this does not quite work yet -- mbp 20080522
2321
self._format = _format
2322
if _control_files is None:
2323
raise ValueError('BzrBranch _control_files is None')
2324
self.control_files = _control_files
2325
self._transport = _control_files._transport
2326
self.repository = _repository
2327
Branch.__init__(self)
2330
if self.name is None:
2331
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2333
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2338
def _get_base(self):
2339
"""Returns the directory containing the control directory."""
2342
base = property(_get_base, doc="The URL for the root of this branch.")
2344
def _get_config(self):
2345
return TransportConfig(self._transport, 'branch.conf')
2347
def is_locked(self):
2348
return self.control_files.is_locked()
2350
def lock_write(self, token=None):
2351
"""Lock the branch for write operations.
2353
:param token: A token to permit reacquiring a previously held and
2355
:return: A BranchWriteLockResult.
2357
if not self.is_locked():
2358
self._note_lock('w')
2359
# All-in-one needs to always unlock/lock.
2360
repo_control = getattr(self.repository, 'control_files', None)
2361
if self.control_files == repo_control or not self.is_locked():
2362
self.repository._warn_if_deprecated(self)
2363
self.repository.lock_write()
2368
return BranchWriteLockResult(self.unlock,
2369
self.control_files.lock_write(token=token))
2372
self.repository.unlock()
2375
def lock_read(self):
2376
"""Lock the branch for read operations.
2378
:return: An object with an unlock method which will release the lock
2381
if not self.is_locked():
2382
self._note_lock('r')
2383
# All-in-one needs to always unlock/lock.
2384
repo_control = getattr(self.repository, 'control_files', None)
2385
if self.control_files == repo_control or not self.is_locked():
2386
self.repository._warn_if_deprecated(self)
2387
self.repository.lock_read()
2392
self.control_files.lock_read()
2396
self.repository.unlock()
2399
@only_raises(errors.LockNotHeld, errors.LockBroken)
2402
self.control_files.unlock()
2404
# All-in-one needs to always unlock/lock.
2405
repo_control = getattr(self.repository, 'control_files', None)
2406
if (self.control_files == repo_control or
2407
not self.control_files.is_locked()):
2408
self.repository.unlock()
2409
if not self.control_files.is_locked():
2410
# we just released the lock
2411
self._clear_cached_state()
2413
def peek_lock_mode(self):
2414
if self.control_files._lock_count == 0:
2417
return self.control_files._lock_mode
2419
def get_physical_lock_status(self):
2420
return self.control_files.get_physical_lock_status()
2423
def print_file(self, file, revision_id):
2424
"""See Branch.print_file."""
2425
return self.repository.print_file(file, revision_id)
2427
def _write_revision_history(self, history):
2428
"""Factored out of set_revision_history.
2430
This performs the actual writing to disk.
2431
It is intended to be called by BzrBranch5.set_revision_history."""
2432
self._transport.put_bytes(
2433
'revision-history', '\n'.join(history),
2434
mode=self.bzrdir._get_file_mode())
2437
def set_revision_history(self, rev_history):
2438
"""See Branch.set_revision_history."""
2439
if 'evil' in debug.debug_flags:
2440
mutter_callsite(3, "set_revision_history scales with history.")
2441
check_not_reserved_id = _mod_revision.check_not_reserved_id
2442
for rev_id in rev_history:
2443
check_not_reserved_id(rev_id)
2444
if Branch.hooks['post_change_branch_tip']:
2445
# Don't calculate the last_revision_info() if there are no hooks
2447
old_revno, old_revid = self.last_revision_info()
2448
if len(rev_history) == 0:
2449
revid = _mod_revision.NULL_REVISION
2451
revid = rev_history[-1]
2452
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2453
self._write_revision_history(rev_history)
2454
self._clear_cached_state()
2455
self._cache_revision_history(rev_history)
2456
for hook in Branch.hooks['set_rh']:
2457
hook(self, rev_history)
2458
if Branch.hooks['post_change_branch_tip']:
2459
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2461
def _synchronize_history(self, destination, revision_id):
2462
"""Synchronize last revision and revision history between branches.
2464
This version is most efficient when the destination is also a
2465
BzrBranch5, but works for BzrBranch6 as long as the revision
2466
history is the true lefthand parent history, and all of the revisions
2467
are in the destination's repository. If not, set_revision_history
2470
:param destination: The branch to copy the history into
2471
:param revision_id: The revision-id to truncate history at. May
2472
be None to copy complete history.
2474
if not isinstance(destination._format, BzrBranchFormat5):
2475
super(BzrBranch, self)._synchronize_history(
2476
destination, revision_id)
2478
if revision_id == _mod_revision.NULL_REVISION:
2481
new_history = self.revision_history()
2482
if revision_id is not None and new_history != []:
2484
new_history = new_history[:new_history.index(revision_id) + 1]
2486
rev = self.repository.get_revision(revision_id)
2487
new_history = rev.get_history(self.repository)[1:]
2488
destination.set_revision_history(new_history)
2491
def set_last_revision_info(self, revno, revision_id):
2492
"""Set the last revision of this branch.
2494
The caller is responsible for checking that the revno is correct
2495
for this revision id.
2497
It may be possible to set the branch last revision to an id not
2498
present in the repository. However, branches can also be
2499
configured to check constraints on history, in which case this may not
2502
revision_id = _mod_revision.ensure_null(revision_id)
2503
# this old format stores the full history, but this api doesn't
2504
# provide it, so we must generate, and might as well check it's
2506
history = self._lefthand_history(revision_id)
2507
if len(history) != revno:
2508
raise AssertionError('%d != %d' % (len(history), revno))
2509
self.set_revision_history(history)
2511
def _gen_revision_history(self):
2512
history = self._transport.get_bytes('revision-history').split('\n')
2513
if history[-1:] == ['']:
2514
# There shouldn't be a trailing newline, but just in case.
2519
def generate_revision_history(self, revision_id, last_rev=None,
2521
"""Create a new revision history that will finish with revision_id.
2523
:param revision_id: the new tip to use.
2524
:param last_rev: The previous last_revision. If not None, then this
2525
must be a ancestory of revision_id, or DivergedBranches is raised.
2526
:param other_branch: The other branch that DivergedBranches should
2527
raise with respect to.
2529
self.set_revision_history(self._lefthand_history(revision_id,
2530
last_rev, other_branch))
2532
def basis_tree(self):
2533
"""See Branch.basis_tree."""
2534
return self.repository.revision_tree(self.last_revision())
2536
def _get_parent_location(self):
2537
_locs = ['parent', 'pull', 'x-pull']
2540
return self._transport.get_bytes(l).strip('\n')
2541
except errors.NoSuchFile:
2545
def _basic_push(self, target, overwrite, stop_revision):
2546
"""Basic implementation of push without bound branches or hooks.
2548
Must be called with source read locked and target write locked.
2550
result = BranchPushResult()
2551
result.source_branch = self
2552
result.target_branch = target
2553
result.old_revno, result.old_revid = target.last_revision_info()
2554
self.update_references(target)
2555
if result.old_revid != self.last_revision():
2556
# We assume that during 'push' this repository is closer than
2558
graph = self.repository.get_graph(target.repository)
2559
target.update_revisions(self, stop_revision,
2560
overwrite=overwrite, graph=graph)
2561
if self._push_should_merge_tags():
2562
result.tag_conflicts = self.tags.merge_to(target.tags,
2564
result.new_revno, result.new_revid = target.last_revision_info()
2567
def get_stacked_on_url(self):
2568
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2570
def set_push_location(self, location):
2571
"""See Branch.set_push_location."""
2572
self.get_config().set_user_option(
2573
'push_location', location,
2574
store=_mod_config.STORE_LOCATION_NORECURSE)
2576
def _set_parent_location(self, url):
2578
self._transport.delete('parent')
2580
self._transport.put_bytes('parent', url + '\n',
2581
mode=self.bzrdir._get_file_mode())
2584
class BzrBranch5(BzrBranch):
2585
"""A format 5 branch. This supports new features over plain branches.
2587
It has support for a master_branch which is the data for bound branches.
2590
def get_bound_location(self):
2592
return self._transport.get_bytes('bound')[:-1]
2593
except errors.NoSuchFile:
2597
def get_master_branch(self, possible_transports=None):
2598
"""Return the branch we are bound to.
2600
:return: Either a Branch, or None
2602
This could memoise the branch, but if thats done
2603
it must be revalidated on each new lock.
2604
So for now we just don't memoise it.
2605
# RBC 20060304 review this decision.
2607
bound_loc = self.get_bound_location()
2611
return Branch.open(bound_loc,
2612
possible_transports=possible_transports)
2613
except (errors.NotBranchError, errors.ConnectionError), e:
2614
raise errors.BoundBranchConnectionFailure(
2618
def set_bound_location(self, location):
2619
"""Set the target where this branch is bound to.
2621
:param location: URL to the target branch
2624
self._transport.put_bytes('bound', location+'\n',
2625
mode=self.bzrdir._get_file_mode())
2628
self._transport.delete('bound')
2629
except errors.NoSuchFile:
2634
def bind(self, other):
2635
"""Bind this branch to the branch other.
2637
This does not push or pull data between the branches, though it does
2638
check for divergence to raise an error when the branches are not
2639
either the same, or one a prefix of the other. That behaviour may not
2640
be useful, so that check may be removed in future.
2642
:param other: The branch to bind to
2645
# TODO: jam 20051230 Consider checking if the target is bound
2646
# It is debatable whether you should be able to bind to
2647
# a branch which is itself bound.
2648
# Committing is obviously forbidden,
2649
# but binding itself may not be.
2650
# Since we *have* to check at commit time, we don't
2651
# *need* to check here
2653
# we want to raise diverged if:
2654
# last_rev is not in the other_last_rev history, AND
2655
# other_last_rev is not in our history, and do it without pulling
2657
self.set_bound_location(other.base)
2661
"""If bound, unbind"""
2662
return self.set_bound_location(None)
2665
def update(self, possible_transports=None):
2666
"""Synchronise this branch with the master branch if any.
2668
:return: None or the last_revision that was pivoted out during the
2671
master = self.get_master_branch(possible_transports)
2672
if master is not None:
2673
old_tip = _mod_revision.ensure_null(self.last_revision())
2674
self.pull(master, overwrite=True)
2675
if self.repository.get_graph().is_ancestor(old_tip,
2676
_mod_revision.ensure_null(self.last_revision())):
2682
class BzrBranch8(BzrBranch5):
2683
"""A branch that stores tree-reference locations."""
2685
def _open_hook(self):
2686
if self._ignore_fallbacks:
2689
url = self.get_stacked_on_url()
2690
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2691
errors.UnstackableBranchFormat):
2694
for hook in Branch.hooks['transform_fallback_location']:
2695
url = hook(self, url)
2697
hook_name = Branch.hooks.get_hook_name(hook)
2698
raise AssertionError(
2699
"'transform_fallback_location' hook %s returned "
2700
"None, not a URL." % hook_name)
2701
self._activate_fallback_location(url)
2703
def __init__(self, *args, **kwargs):
2704
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2705
super(BzrBranch8, self).__init__(*args, **kwargs)
2706
self._last_revision_info_cache = None
2707
self._reference_info = None
2709
def _clear_cached_state(self):
2710
super(BzrBranch8, self)._clear_cached_state()
2711
self._last_revision_info_cache = None
2712
self._reference_info = None
2714
def _last_revision_info(self):
2715
revision_string = self._transport.get_bytes('last-revision')
2716
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2717
revision_id = cache_utf8.get_cached_utf8(revision_id)
2719
return revno, revision_id
2721
def _write_last_revision_info(self, revno, revision_id):
2722
"""Simply write out the revision id, with no checks.
2724
Use set_last_revision_info to perform this safely.
2726
Does not update the revision_history cache.
2727
Intended to be called by set_last_revision_info and
2728
_write_revision_history.
2730
revision_id = _mod_revision.ensure_null(revision_id)
2731
out_string = '%d %s\n' % (revno, revision_id)
2732
self._transport.put_bytes('last-revision', out_string,
2733
mode=self.bzrdir._get_file_mode())
2736
def set_last_revision_info(self, revno, revision_id):
2737
revision_id = _mod_revision.ensure_null(revision_id)
2738
old_revno, old_revid = self.last_revision_info()
2739
if self._get_append_revisions_only():
2740
self._check_history_violation(revision_id)
2741
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2742
self._write_last_revision_info(revno, revision_id)
2743
self._clear_cached_state()
2744
self._last_revision_info_cache = revno, revision_id
2745
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2747
def _synchronize_history(self, destination, revision_id):
2748
"""Synchronize last revision and revision history between branches.
2750
:see: Branch._synchronize_history
2752
# XXX: The base Branch has a fast implementation of this method based
2753
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2754
# that uses set_revision_history. This class inherits from BzrBranch5,
2755
# but wants the fast implementation, so it calls
2756
# Branch._synchronize_history directly.
2757
Branch._synchronize_history(self, destination, revision_id)
2759
def _check_history_violation(self, revision_id):
2760
last_revision = _mod_revision.ensure_null(self.last_revision())
2761
if _mod_revision.is_null(last_revision):
2763
if last_revision not in self._lefthand_history(revision_id):
2764
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2766
def _gen_revision_history(self):
2767
"""Generate the revision history from last revision
2769
last_revno, last_revision = self.last_revision_info()
2770
self._extend_partial_history(stop_index=last_revno-1)
2771
return list(reversed(self._partial_revision_history_cache))
2773
def _write_revision_history(self, history):
2774
"""Factored out of set_revision_history.
2776
This performs the actual writing to disk, with format-specific checks.
2777
It is intended to be called by BzrBranch5.set_revision_history.
2779
if len(history) == 0:
2780
last_revision = 'null:'
2782
if history != self._lefthand_history(history[-1]):
2783
raise errors.NotLefthandHistory(history)
2784
last_revision = history[-1]
2785
if self._get_append_revisions_only():
2786
self._check_history_violation(last_revision)
2787
self._write_last_revision_info(len(history), last_revision)
2790
def _set_parent_location(self, url):
2791
"""Set the parent branch"""
2792
self._set_config_location('parent_location', url, make_relative=True)
2795
def _get_parent_location(self):
2796
"""Set the parent branch"""
2797
return self._get_config_location('parent_location')
2800
def _set_all_reference_info(self, info_dict):
2801
"""Replace all reference info stored in a branch.
2803
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2806
writer = rio.RioWriter(s)
2807
for key, (tree_path, branch_location) in info_dict.iteritems():
2808
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2809
branch_location=branch_location)
2810
writer.write_stanza(stanza)
2811
self._transport.put_bytes('references', s.getvalue())
2812
self._reference_info = info_dict
2815
def _get_all_reference_info(self):
2816
"""Return all the reference info stored in a branch.
2818
:return: A dict of {file_id: (tree_path, branch_location)}
2820
if self._reference_info is not None:
2821
return self._reference_info
2822
rio_file = self._transport.get('references')
2824
stanzas = rio.read_stanzas(rio_file)
2825
info_dict = dict((s['file_id'], (s['tree_path'],
2826
s['branch_location'])) for s in stanzas)
2829
self._reference_info = info_dict
2832
def set_reference_info(self, file_id, tree_path, branch_location):
2833
"""Set the branch location to use for a tree reference.
2835
:param file_id: The file-id of the tree reference.
2836
:param tree_path: The path of the tree reference in the tree.
2837
:param branch_location: The location of the branch to retrieve tree
2840
info_dict = self._get_all_reference_info()
2841
info_dict[file_id] = (tree_path, branch_location)
2842
if None in (tree_path, branch_location):
2843
if tree_path is not None:
2844
raise ValueError('tree_path must be None when branch_location'
2846
if branch_location is not None:
2847
raise ValueError('branch_location must be None when tree_path'
2849
del info_dict[file_id]
2850
self._set_all_reference_info(info_dict)
2852
def get_reference_info(self, file_id):
2853
"""Get the tree_path and branch_location for a tree reference.
2855
:return: a tuple of (tree_path, branch_location)
2857
return self._get_all_reference_info().get(file_id, (None, None))
2859
def reference_parent(self, file_id, path, possible_transports=None):
2860
"""Return the parent branch for a tree-reference file_id.
2862
:param file_id: The file_id of the tree reference
2863
:param path: The path of the file_id in the tree
2864
:return: A branch associated with the file_id
2866
branch_location = self.get_reference_info(file_id)[1]
2867
if branch_location is None:
2868
return Branch.reference_parent(self, file_id, path,
2869
possible_transports)
2870
branch_location = urlutils.join(self.user_url, branch_location)
2871
return Branch.open(branch_location,
2872
possible_transports=possible_transports)
2874
def set_push_location(self, location):
2875
"""See Branch.set_push_location."""
2876
self._set_config_location('push_location', location)
2878
def set_bound_location(self, location):
2879
"""See Branch.set_push_location."""
2881
config = self.get_config()
2882
if location is None:
2883
if config.get_user_option('bound') != 'True':
2886
config.set_user_option('bound', 'False', warn_masked=True)
2889
self._set_config_location('bound_location', location,
2891
config.set_user_option('bound', 'True', warn_masked=True)
2894
def _get_bound_location(self, bound):
2895
"""Return the bound location in the config file.
2897
Return None if the bound parameter does not match"""
2898
config = self.get_config()
2899
config_bound = (config.get_user_option('bound') == 'True')
2900
if config_bound != bound:
2902
return self._get_config_location('bound_location', config=config)
2904
def get_bound_location(self):
2905
"""See Branch.set_push_location."""
2906
return self._get_bound_location(True)
2908
def get_old_bound_location(self):
2909
"""See Branch.get_old_bound_location"""
2910
return self._get_bound_location(False)
2912
def get_stacked_on_url(self):
2913
# you can always ask for the URL; but you might not be able to use it
2914
# if the repo can't support stacking.
2915
## self._check_stackable_repo()
2916
stacked_url = self._get_config_location('stacked_on_location')
2917
if stacked_url is None:
2918
raise errors.NotStacked(self)
2921
def _get_append_revisions_only(self):
2922
return self.get_config(
2923
).get_user_option_as_bool('append_revisions_only')
2926
def generate_revision_history(self, revision_id, last_rev=None,
2928
"""See BzrBranch5.generate_revision_history"""
2929
history = self._lefthand_history(revision_id, last_rev, other_branch)
2930
revno = len(history)
2931
self.set_last_revision_info(revno, revision_id)
2934
def get_rev_id(self, revno, history=None):
2935
"""Find the revision id of the specified revno."""
2937
return _mod_revision.NULL_REVISION
2939
last_revno, last_revision_id = self.last_revision_info()
2940
if revno <= 0 or revno > last_revno:
2941
raise errors.NoSuchRevision(self, revno)
2943
if history is not None:
2944
return history[revno - 1]
2946
index = last_revno - revno
2947
if len(self._partial_revision_history_cache) <= index:
2948
self._extend_partial_history(stop_index=index)
2949
if len(self._partial_revision_history_cache) > index:
2950
return self._partial_revision_history_cache[index]
2952
raise errors.NoSuchRevision(self, revno)
2955
def revision_id_to_revno(self, revision_id):
2956
"""Given a revision id, return its revno"""
2957
if _mod_revision.is_null(revision_id):
2960
index = self._partial_revision_history_cache.index(revision_id)
2962
self._extend_partial_history(stop_revision=revision_id)
2963
index = len(self._partial_revision_history_cache) - 1
2964
if self._partial_revision_history_cache[index] != revision_id:
2965
raise errors.NoSuchRevision(self, revision_id)
2966
return self.revno() - index
2969
class BzrBranch7(BzrBranch8):
2970
"""A branch with support for a fallback repository."""
2972
def set_reference_info(self, file_id, tree_path, branch_location):
2973
Branch.set_reference_info(self, file_id, tree_path, branch_location)
2975
def get_reference_info(self, file_id):
2976
Branch.get_reference_info(self, file_id)
2978
def reference_parent(self, file_id, path, possible_transports=None):
2979
return Branch.reference_parent(self, file_id, path,
2980
possible_transports)
2983
class BzrBranch6(BzrBranch7):
2984
"""See BzrBranchFormat6 for the capabilities of this branch.
2986
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2990
def get_stacked_on_url(self):
2991
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1944
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
2994
1947
######################################################################
3262
2205
if graph is None:
3263
2206
graph = self.target.repository.get_graph()
3264
2207
this_revno, this_last_revision = \
3265
self.target.last_revision_info()
3266
stop_revno = graph.find_distance_to_null(stop_revision,
3267
[(other_last_revision, other_revno),
3268
(this_last_revision, this_revno)])
2208
self.target.last_revision_info()
2209
stop_revno = graph.find_distance_to_null(
2210
stop_revision, [(other_last_revision, other_revno),
2211
(this_last_revision, this_revno)])
3269
2212
self.target.set_last_revision_info(stop_revno, stop_revision)
3271
self.source.unlock()
3273
2214
def pull(self, overwrite=False, stop_revision=None,
3274
possible_transports=None, _hook_master=None, run_hooks=True,
2215
possible_transports=None, run_hooks=True,
3275
2216
_override_hook_target=None, local=False):
2217
"""Pull from source into self, updating my master if any.
2219
:param run_hooks: Private parameter - if false, this branch
2220
is being called because it's the master of the primary branch,
2221
so it should not run its hooks.
2223
with self.target.lock_write():
2224
bound_location = self.target.get_bound_location()
2225
if local and not bound_location:
2226
raise errors.LocalRequiresBoundBranch()
2227
master_branch = None
2228
source_is_master = False
2230
# bound_location comes from a config file, some care has to be
2231
# taken to relate it to source.user_url
2232
normalized = urlutils.normalize_url(bound_location)
2234
relpath = self.source.user_transport.relpath(normalized)
2235
source_is_master = (relpath == '')
2236
except (errors.PathNotChild, urlutils.InvalidURL):
2237
source_is_master = False
2238
if not local and bound_location and not source_is_master:
2239
# not pulling from master, so we need to update master.
2240
master_branch = self.target.get_master_branch(
2241
possible_transports)
2242
master_branch.lock_write()
2245
# pull from source into master.
2247
self.source, overwrite, stop_revision, run_hooks=False)
2249
overwrite, stop_revision, _hook_master=master_branch,
2250
run_hooks=run_hooks,
2251
_override_hook_target=_override_hook_target,
2252
merge_tags_to_master=not source_is_master)
2255
master_branch.unlock()
2257
def push(self, overwrite=False, stop_revision=None, lossy=False,
2258
_override_hook_source_branch=None):
2259
"""See InterBranch.push.
2261
This is the basic concrete implementation of push()
2263
:param _override_hook_source_branch: If specified, run the hooks
2264
passing this Branch as the source, rather than self. This is for
2265
use of RemoteBranch, where push is delegated to the underlying
2269
raise errors.LossyPushToSameVCS(self.source, self.target)
2270
# TODO: Public option to disable running hooks - should be trivial but
2274
if _override_hook_source_branch:
2275
result.source_branch = _override_hook_source_branch
2276
for hook in Branch.hooks['post_push']:
2279
with self.source.lock_read(), self.target.lock_write():
2280
bound_location = self.target.get_bound_location()
2281
if bound_location and self.target.base != bound_location:
2282
# there is a master branch.
2284
# XXX: Why the second check? Is it even supported for a branch
2285
# to be bound to itself? -- mbp 20070507
2286
master_branch = self.target.get_master_branch()
2287
with master_branch.lock_write():
2288
# push into the master from the source branch.
2289
master_inter = InterBranch.get(self.source, master_branch)
2290
master_inter._basic_push(overwrite, stop_revision)
2291
# and push into the target branch from the source. Note
2292
# that we push from the source branch again, because it's
2293
# considered the highest bandwidth repository.
2294
result = self._basic_push(overwrite, stop_revision)
2295
result.master_branch = master_branch
2296
result.local_branch = self.target
2299
master_branch = None
2301
result = self._basic_push(overwrite, stop_revision)
2302
# TODO: Why set master_branch and local_branch if there's no
2303
# binding? Maybe cleaner to just leave them unset? -- mbp
2305
result.master_branch = self.target
2306
result.local_branch = None
2310
def _basic_push(self, overwrite, stop_revision):
2311
"""Basic implementation of push without bound branches or hooks.
2313
Must be called with source read locked and target write locked.
2315
result = BranchPushResult()
2316
result.source_branch = self.source
2317
result.target_branch = self.target
2318
result.old_revno, result.old_revid = self.target.last_revision_info()
2319
self.source.update_references(self.target)
2320
overwrite = _fix_overwrite_type(overwrite)
2321
if result.old_revid != stop_revision:
2322
# We assume that during 'push' this repository is closer than
2324
graph = self.source.repository.get_graph(self.target.repository)
2325
self._update_revisions(
2326
stop_revision, overwrite=("history" in overwrite), graph=graph)
2327
if self.source._push_should_merge_tags():
2328
result.tag_updates, result.tag_conflicts = (
2329
self.source.tags.merge_to(
2330
self.target.tags, "tags" in overwrite))
2331
result.new_revno, result.new_revid = self.target.last_revision_info()
2334
def _pull(self, overwrite=False, stop_revision=None,
2335
possible_transports=None, _hook_master=None, run_hooks=True,
2336
_override_hook_target=None, local=False,
2337
merge_tags_to_master=True):
3276
2338
"""See Branch.pull.
2340
This function is the core worker, used by GenericInterBranch.pull to
2341
avoid duplication when pulling source->master and source->local.
3278
2343
:param _hook_master: Private parameter - set the branch to
3279
2344
be supplied as the master to pull hooks.
3280
2345
:param run_hooks: Private parameter - if false, this branch
3281
2346
is being called because it's the master of the primary branch,
3282
2347
so it should not run its hooks.
2348
is being called because it's the master of the primary branch,
2349
so it should not run its hooks.
3283
2350
:param _override_hook_target: Private parameter - set the branch to be
3284
2351
supplied as the target_branch to pull hooks.
3285
2352
:param local: Only update the local branch, and not the bound branch.