760
801
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)
802
raise UnstackableBranchFormat(self._format, self.user_url)
803
with self.lock_write():
804
# XXX: Changing from one fallback repository to another does not
805
# check that all the data you need is present in the new fallback.
806
# Possibly it should.
807
self._check_stackable_repo()
810
self.get_stacked_on_url()
811
except (errors.NotStacked, UnstackableBranchFormat,
812
errors.UnstackableRepositoryFormat):
816
self._activate_fallback_location(
817
url, possible_transports=[self.controldir.root_transport])
818
# write this out after the repository is stacked to avoid setting a
819
# stacked config that doesn't work.
820
self._set_config_location('stacked_on_location', url)
779
822
def _unstack(self):
780
823
"""Change a branch to be unstacked, copying data as needed.
782
825
Don't call this directly, use set_stacked_on_url(None).
784
pb = ui.ui_factory.nested_progress_bar()
786
pb.update("Unstacking")
827
with ui.ui_factory.nested_progress_bar() as pb:
828
pb.update(gettext("Unstacking"))
787
829
# The basic approach here is to fetch the tip of the branch,
788
830
# including all available ghosts, from the existing stacked
789
# repository into a new repository object without the fallbacks.
831
# repository into a new repository object without the fallbacks.
791
833
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
792
834
# correct for CHKMap repostiories
793
835
old_repository = self.repository
794
836
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
837
raise AssertionError(
838
"can't cope with fallback repositories "
839
"of %r (fallbacks: %r)" % (
840
old_repository, old_repository._fallback_repositories))
841
# Open the new repository object.
842
# Repositories don't offer an interface to remove fallback
843
# repositories today; take the conceptually simpler option and just
844
# reopen it. We reopen it starting from the URL so that we
845
# get a separate connection for RemoteRepositories and can
846
# stream from one of them to the other. This does mean doing
847
# separate SSH connection setup, but unstacking is not a
848
# common operation so it's tolerable.
849
new_bzrdir = controldir.ControlDir.open(
850
self.controldir.root_transport.base)
851
new_repository = new_bzrdir.find_repository()
852
if new_repository._fallback_repositories:
853
raise AssertionError(
854
"didn't expect %r to have fallback_repositories"
855
% (self.repository,))
856
# Replace self.repository with the new repository.
857
# Do our best to transfer the lock state (i.e. lock-tokens and
858
# lock count) of self.repository to the new repository.
859
lock_token = old_repository.lock_write().repository_token
860
self.repository = new_repository
861
if isinstance(self, remote.RemoteBranch):
862
# Remote branches can have a second reference to the old
863
# repository that need to be replaced.
864
if self._real_branch is not None:
865
self._real_branch.repository = new_repository
866
self.repository.lock_write(token=lock_token)
867
if lock_token is not None:
868
old_repository.leave_lock_in_place()
798
869
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
870
if lock_token is not None:
871
# XXX: self.repository.leave_lock_in_place() before this
872
# function will not be preserved. Fortunately that doesn't
873
# affect the current default format (2a), and would be a
874
# corner-case anyway.
875
# - Andrew Bennetts, 2010/06/30
876
self.repository.dont_leave_lock_in_place()
880
old_repository.unlock()
881
except errors.LockNotHeld:
884
if old_lock_count == 0:
885
raise AssertionError(
886
'old_repository should have been locked at least once.')
887
for i in range(old_lock_count - 1):
818
888
self.repository.lock_write()
889
# Fetch from the old repository into the new.
890
with old_repository.lock_read():
819
891
# XXX: If you unstack a branch while it has a working tree
820
892
# with a pending merge, the pending-merged revisions will no
821
893
# 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,
895
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
896
except errors.TagsNotSupported:
897
tags_to_fetch = set()
898
fetch_spec = vf_search.NotInOtherForRevs(
899
self.repository, old_repository,
900
required_ids=[self.last_revision()],
901
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
902
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
842
904
def _cache_revision_history(self, rev_history):
843
905
"""Set the cached revision history to rev_history.
921
978
"""Return last revision id, or NULL_REVISION."""
922
979
return self.last_revision_info()[1]
925
981
def last_revision_info(self):
926
982
"""Return information about the last revision.
928
984
: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):
986
with self.lock_read():
987
if self._last_revision_info_cache is None:
988
self._last_revision_info_cache = (
989
self._read_last_revision_info())
990
return self._last_revision_info_cache
992
def _read_last_revision_info(self):
993
raise NotImplementedError(self._read_last_revision_info)
995
def import_last_revision_info_and_tags(self, source, revno, revid,
982
997
"""Set the last revision info, importing from another repo if necessary.
984
999
This is used by the bound branch code to upload a revision to
985
1000
the master branch first before updating the tip of the local branch.
1001
Revisions referenced by source's tags are also transferred.
987
:param source_repo: Source repository to optionally fetch from
1003
:param source: Source branch to optionally fetch from
988
1004
:param revno: Revision number of the new tip
989
1005
:param revid: Revision id of the new tip
1006
:param lossy: Whether to discard metadata that can not be
1007
natively represented
1008
:return: Tuple with the new revision number and revision id
1009
(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)
1011
if not self.repository.has_same_location(source.repository):
1012
self.fetch(source, revid)
993
1013
self.set_last_revision_info(revno, revid)
1014
return (revno, revid)
995
1016
def revision_id_to_revno(self, revision_id):
996
1017
"""Given a revision id, return its revno"""
997
1018
if _mod_revision.is_null(revision_id):
999
history = self.revision_history()
1020
history = self._revision_history()
1001
1022
return history.index(revision_id) + 1
1002
1023
except ValueError:
1003
1024
raise errors.NoSuchRevision(self, revision_id)
1006
1026
def get_rev_id(self, revno, history=None):
1007
1027
"""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]
1028
with self.lock_read():
1030
return _mod_revision.NULL_REVISION
1031
last_revno, last_revid = self.last_revision_info()
1032
if revno == last_revno:
1034
if revno <= 0 or revno > last_revno:
1035
raise errors.NoSuchRevision(self, revno)
1036
distance_from_last = last_revno - revno
1037
if len(self._partial_revision_history_cache) <= distance_from_last:
1038
self._extend_partial_history(distance_from_last)
1039
return self._partial_revision_history_cache[distance_from_last]
1021
1041
def pull(self, source, overwrite=False, stop_revision=None,
1022
1042
possible_transports=None, *args, **kwargs):
1023
1043
"""Mirror source into this branch.
1910
1856
return self.__dict__ == other.__dict__
1912
1858
def __repr__(self):
1913
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1914
self.control_dir, self.to_branch,
1859
return "<%s for %s to (%s, %s)>" % (
1860
self.__class__.__name__, self.control_dir, self.to_branch,
1915
1861
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)
1864
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1865
"""Branch format registry."""
1867
def __init__(self, other_registry=None):
1868
super(BranchFormatRegistry, self).__init__(other_registry)
1869
self._default_format = None
1870
self._default_format_key = None
1872
def get_default(self):
1873
"""Return the current default format."""
1874
if (self._default_format_key is not None
1875
and self._default_format is None):
1876
self._default_format = self.get(self._default_format_key)
1877
return self._default_format
1879
def set_default(self, format):
1880
"""Set the default format."""
1881
self._default_format = format
1882
self._default_format_key = None
1884
def set_default_key(self, format_string):
1885
"""Set the default format by its format string."""
1886
self._default_format_key = format_string
1887
self._default_format = None
2252
1890
network_format_registry = registry.FormatRegistry()
2257
1895
BranchFormat.network_name() for more detail.
1898
format_registry = BranchFormatRegistry(network_format_registry)
2261
1901
# formats which have no format string are not discoverable
2262
1902
# 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):
1903
format_registry.register_lazy(
1904
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1906
format_registry.register_lazy(
1907
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1908
"breezy.bzr.branch", "BzrBranchFormat6")
1909
format_registry.register_lazy(
1910
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1911
"breezy.bzr.branch", "BzrBranchFormat7")
1912
format_registry.register_lazy(
1913
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1914
"breezy.bzr.branch", "BzrBranchFormat8")
1915
format_registry.register_lazy(
1916
b"Bazaar-NG Branch Reference Format 1\n",
1917
"breezy.bzr.branch", "BranchReferenceFormat")
1919
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1922
class BranchWriteLockResult(LogicalLockResult):
2280
1923
"""The result of write locking a branch.
2282
:ivar branch_token: The token obtained from the underlying branch lock, or
1925
:ivar token: The token obtained from the underlying branch lock, or
2284
1927
: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)
1931
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
2994
1934
######################################################################
3262
2207
if graph is None:
3263
2208
graph = self.target.repository.get_graph()
3264
2209
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)])
2210
self.target.last_revision_info()
2211
stop_revno = graph.find_distance_to_null(
2212
stop_revision, [(other_last_revision, other_revno),
2213
(this_last_revision, this_revno)])
3269
2214
self.target.set_last_revision_info(stop_revno, stop_revision)
3271
self.source.unlock()
3273
2216
def pull(self, overwrite=False, stop_revision=None,
3274
possible_transports=None, _hook_master=None, run_hooks=True,
3275
_override_hook_target=None, local=False):
2217
possible_transports=None, run_hooks=True,
2218
_override_hook_target=None, local=False,
2220
"""Pull from source into self, updating my master if any.
2222
:param run_hooks: Private parameter - if false, this branch
2223
is being called because it's the master of the primary branch,
2224
so it should not run its hooks.
2226
with cleanup.ExitStack() as exit_stack:
2227
exit_stack.enter_context(self.target.lock_write())
2228
bound_location = self.target.get_bound_location()
2229
if local and not bound_location:
2230
raise errors.LocalRequiresBoundBranch()
2231
master_branch = None
2232
source_is_master = False
2234
# bound_location comes from a config file, some care has to be
2235
# taken to relate it to source.user_url
2236
normalized = urlutils.normalize_url(bound_location)
2238
relpath = self.source.user_transport.relpath(normalized)
2239
source_is_master = (relpath == '')
2240
except (errors.PathNotChild, urlutils.InvalidURL):
2241
source_is_master = False
2242
if not local and bound_location and not source_is_master:
2243
# not pulling from master, so we need to update master.
2244
master_branch = self.target.get_master_branch(
2245
possible_transports)
2246
exit_stack.enter_context(master_branch.lock_write())
2248
# pull from source into master.
2250
self.source, overwrite, stop_revision, run_hooks=False,
2251
tag_selector=tag_selector)
2253
overwrite, stop_revision, _hook_master=master_branch,
2254
run_hooks=run_hooks,
2255
_override_hook_target=_override_hook_target,
2256
merge_tags_to_master=not source_is_master,
2257
tag_selector=tag_selector)
2259
def push(self, overwrite=False, stop_revision=None, lossy=False,
2260
_override_hook_source_branch=None, tag_selector=None):
2261
"""See InterBranch.push.
2263
This is the basic concrete implementation of push()
2265
:param _override_hook_source_branch: If specified, run the hooks
2266
passing this Branch as the source, rather than self. This is for
2267
use of RemoteBranch, where push is delegated to the underlying
2271
raise errors.LossyPushToSameVCS(self.source, self.target)
2272
# TODO: Public option to disable running hooks - should be trivial but
2276
if _override_hook_source_branch:
2277
result.source_branch = _override_hook_source_branch
2278
for hook in Branch.hooks['post_push']:
2281
with self.source.lock_read(), self.target.lock_write():
2282
bound_location = self.target.get_bound_location()
2283
if bound_location and self.target.base != bound_location:
2284
# there is a master branch.
2286
# XXX: Why the second check? Is it even supported for a branch
2287
# to be bound to itself? -- mbp 20070507
2288
master_branch = self.target.get_master_branch()
2289
with master_branch.lock_write():
2290
# push into the master from the source branch.
2291
master_inter = InterBranch.get(self.source, master_branch)
2292
master_inter._basic_push(
2293
overwrite, stop_revision, tag_selector=tag_selector)
2294
# and push into the target branch from the source. Note
2295
# that we push from the source branch again, because it's
2296
# considered the highest bandwidth repository.
2297
result = self._basic_push(
2298
overwrite, stop_revision, tag_selector=tag_selector)
2299
result.master_branch = master_branch
2300
result.local_branch = self.target
2303
master_branch = None
2305
result = self._basic_push(
2306
overwrite, stop_revision, tag_selector=tag_selector)
2307
# TODO: Why set master_branch and local_branch if there's no
2308
# binding? Maybe cleaner to just leave them unset? -- mbp
2310
result.master_branch = self.target
2311
result.local_branch = None
2315
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
2316
"""Basic implementation of push without bound branches or hooks.
2318
Must be called with source read locked and target write locked.
2320
result = BranchPushResult()
2321
result.source_branch = self.source
2322
result.target_branch = self.target
2323
result.old_revno, result.old_revid = self.target.last_revision_info()
2324
overwrite = _fix_overwrite_type(overwrite)
2325
if result.old_revid != stop_revision:
2326
# We assume that during 'push' this repository is closer than
2328
graph = self.source.repository.get_graph(self.target.repository)
2329
self._update_revisions(
2330
stop_revision, overwrite=("history" in overwrite), graph=graph)
2331
if self.source._push_should_merge_tags():
2332
result.tag_updates, result.tag_conflicts = (
2333
self.source.tags.merge_to(
2334
self.target.tags, "tags" in overwrite, selector=tag_selector))
2335
self.update_references()
2336
result.new_revno, result.new_revid = self.target.last_revision_info()
2339
def _pull(self, overwrite=False, stop_revision=None,
2340
possible_transports=None, _hook_master=None, run_hooks=True,
2341
_override_hook_target=None, local=False,
2342
merge_tags_to_master=True, tag_selector=None):
3276
2343
"""See Branch.pull.
2345
This function is the core worker, used by GenericInterBranch.pull to
2346
avoid duplication when pulling source->master and source->local.
3278
2348
:param _hook_master: Private parameter - set the branch to
3279
2349
be supplied as the master to pull hooks.
3280
2350
:param run_hooks: Private parameter - if false, this branch
3281
2351
is being called because it's the master of the primary branch,
3282
2352
so it should not run its hooks.
2353
is being called because it's the master of the primary branch,
2354
so it should not run its hooks.
3283
2355
:param _override_hook_target: Private parameter - set the branch to be
3284
2356
supplied as the target_branch to pull hooks.
3285
2357
:param local: Only update the local branch, and not the bound branch.