1941
1850
return self.__dict__ == other.__dict__
1943
1852
def __repr__(self):
1944
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1945
self.control_dir, self.to_branch,
1853
return "<%s for %s to (%s, %s)>" % (
1854
self.__class__.__name__, self.control_dir, self.to_branch,
1946
1855
self.revision_id)
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1950
"""Base class for branch formats that live in meta directories.
1954
BranchFormat.__init__(self)
1955
bzrdir.BzrFormat.__init__(self)
1958
def find_format(klass, controldir, name=None):
1959
"""Return the format for the branch object in controldir."""
1961
transport = controldir.get_branch_transport(None, name=name)
1962
except errors.NoSuchFile:
1963
raise errors.NotBranchError(path=name, bzrdir=controldir)
1965
format_string = transport.get_bytes("format")
1966
except errors.NoSuchFile:
1967
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1968
return klass._find_format(format_registry, 'branch', format_string)
1970
def _branch_class(self):
1971
"""What class to instantiate on open calls."""
1972
raise NotImplementedError(self._branch_class)
1974
def _get_initial_config(self, append_revisions_only=None):
1975
if append_revisions_only:
1976
return "append_revisions_only = True\n"
1978
# Avoid writing anything if append_revisions_only is disabled,
1979
# as that is the default.
1982
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1984
"""Initialize a branch in a control dir, with specified files
1986
:param a_bzrdir: The bzrdir to initialize the branch in
1987
:param utf8_files: The files to create as a list of
1988
(filename, content) tuples
1989
:param name: Name of colocated branch to create, if any
1990
:return: a branch in this format
1993
name = a_bzrdir._get_selected_branch()
1994
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1995
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1996
control_files = lockable_files.LockableFiles(branch_transport,
1997
'lock', lockdir.LockDir)
1998
control_files.create_lock()
1999
control_files.lock_write()
2001
utf8_files += [('format', self.as_string())]
2002
for (filename, content) in utf8_files:
2003
branch_transport.put_bytes(
2005
mode=a_bzrdir._get_file_mode())
2007
control_files.unlock()
2008
branch = self.open(a_bzrdir, name, _found=True,
2009
found_repository=repository)
2010
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2013
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2014
found_repository=None, possible_transports=None):
2015
"""See BranchFormat.open()."""
2017
name = a_bzrdir._get_selected_branch()
2019
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2020
if format.__class__ != self.__class__:
2021
raise AssertionError("wrong format %r found for %r" %
2023
transport = a_bzrdir.get_branch_transport(None, name=name)
2025
control_files = lockable_files.LockableFiles(transport, 'lock',
2027
if found_repository is None:
2028
found_repository = a_bzrdir.find_repository()
2029
return self._branch_class()(_format=self,
2030
_control_files=control_files,
2033
_repository=found_repository,
2034
ignore_fallbacks=ignore_fallbacks,
2035
possible_transports=possible_transports)
2036
except errors.NoSuchFile:
2037
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2040
def _matchingbzrdir(self):
2041
ret = bzrdir.BzrDirMetaFormat1()
2042
ret.set_branch_format(self)
2045
def supports_tags(self):
2048
def supports_leaving_lock(self):
2051
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2053
BranchFormat.check_support_status(self,
2054
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2056
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2057
recommend_upgrade=recommend_upgrade, basedir=basedir)
2060
class BzrBranchFormat6(BranchFormatMetadir):
2061
"""Branch format with last-revision and tags.
2063
Unlike previous formats, this has no explicit revision history. Instead,
2064
this just stores the last-revision, and the left-hand history leading
2065
up to there is the history.
2067
This format was introduced in bzr 0.15
2068
and became the default in 0.91.
2071
def _branch_class(self):
2075
def get_format_string(cls):
2076
"""See BranchFormat.get_format_string()."""
2077
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2079
def get_format_description(self):
2080
"""See BranchFormat.get_format_description()."""
2081
return "Branch format 6"
2083
def initialize(self, a_bzrdir, name=None, repository=None,
2084
append_revisions_only=None):
2085
"""Create a branch of this format in a_bzrdir."""
2086
utf8_files = [('last-revision', '0 null:\n'),
2088
self._get_initial_config(append_revisions_only)),
2091
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2093
def make_tags(self, branch):
2094
"""See bzrlib.branch.BranchFormat.make_tags()."""
2095
return _mod_tag.BasicTags(branch)
2097
def supports_set_append_revisions_only(self):
2101
class BzrBranchFormat8(BranchFormatMetadir):
2102
"""Metadir format supporting storing locations of subtree branches."""
2104
def _branch_class(self):
2108
def get_format_string(cls):
2109
"""See BranchFormat.get_format_string()."""
2110
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2112
def get_format_description(self):
2113
"""See BranchFormat.get_format_description()."""
2114
return "Branch format 8"
2116
def initialize(self, a_bzrdir, name=None, repository=None,
2117
append_revisions_only=None):
2118
"""Create a branch of this format in a_bzrdir."""
2119
utf8_files = [('last-revision', '0 null:\n'),
2121
self._get_initial_config(append_revisions_only)),
2125
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2127
def make_tags(self, branch):
2128
"""See bzrlib.branch.BranchFormat.make_tags()."""
2129
return _mod_tag.BasicTags(branch)
2131
def supports_set_append_revisions_only(self):
2134
def supports_stacking(self):
2137
supports_reference_locations = True
2140
class BzrBranchFormat7(BranchFormatMetadir):
2141
"""Branch format with last-revision, tags, and a stacked location pointer.
2143
The stacked location pointer is passed down to the repository and requires
2144
a repository format with supports_external_lookups = True.
2146
This format was introduced in bzr 1.6.
2149
def initialize(self, a_bzrdir, name=None, repository=None,
2150
append_revisions_only=None):
2151
"""Create a branch of this format in a_bzrdir."""
2152
utf8_files = [('last-revision', '0 null:\n'),
2154
self._get_initial_config(append_revisions_only)),
2157
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2159
def _branch_class(self):
2163
def get_format_string(cls):
2164
"""See BranchFormat.get_format_string()."""
2165
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2167
def get_format_description(self):
2168
"""See BranchFormat.get_format_description()."""
2169
return "Branch format 7"
2171
def supports_set_append_revisions_only(self):
2174
def supports_stacking(self):
2177
def make_tags(self, branch):
2178
"""See bzrlib.branch.BranchFormat.make_tags()."""
2179
return _mod_tag.BasicTags(branch)
2181
supports_reference_locations = False
2184
class BranchReferenceFormat(BranchFormatMetadir):
2185
"""Bzr branch reference format.
2187
Branch references are used in implementing checkouts, they
2188
act as an alias to the real branch which is at some other url.
2196
def get_format_string(cls):
2197
"""See BranchFormat.get_format_string()."""
2198
return "Bazaar-NG Branch Reference Format 1\n"
2200
def get_format_description(self):
2201
"""See BranchFormat.get_format_description()."""
2202
return "Checkout reference format 1"
2204
def get_reference(self, a_bzrdir, name=None):
2205
"""See BranchFormat.get_reference()."""
2206
transport = a_bzrdir.get_branch_transport(None, name=name)
2207
return transport.get_bytes('location')
2209
def set_reference(self, a_bzrdir, name, to_branch):
2210
"""See BranchFormat.set_reference()."""
2211
transport = a_bzrdir.get_branch_transport(None, name=name)
2212
location = transport.put_bytes('location', to_branch.base)
2214
def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
repository=None, append_revisions_only=None):
2216
"""Create a branch of this format in a_bzrdir."""
2217
if target_branch is None:
2218
# this format does not implement branch itself, thus the implicit
2219
# creation contract must see it as uninitializable
2220
raise errors.UninitializableFormat(self)
2221
mutter('creating branch reference in %s', a_bzrdir.user_url)
2222
if a_bzrdir._format.fixed_components:
2223
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2225
name = a_bzrdir._get_selected_branch()
2226
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2227
branch_transport.put_bytes('location',
2228
target_branch.user_url)
2229
branch_transport.put_bytes('format', self.as_string())
2230
branch = self.open(a_bzrdir, name, _found=True,
2231
possible_transports=[target_branch.bzrdir.root_transport])
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2235
def _make_reference_clone_function(format, a_branch):
2236
"""Create a clone() routine for a branch dynamically."""
2237
def clone(to_bzrdir, revision_id=None,
2238
repository_policy=None):
2239
"""See Branch.clone()."""
2240
return format.initialize(to_bzrdir, target_branch=a_branch)
2241
# cannot obey revision_id limits when cloning a reference ...
2242
# FIXME RBC 20060210 either nuke revision_id for clone, or
2243
# emit some sort of warning/error to the caller ?!
2246
def open(self, a_bzrdir, name=None, _found=False, location=None,
2247
possible_transports=None, ignore_fallbacks=False,
2248
found_repository=None):
2249
"""Return the branch that the branch reference in a_bzrdir points at.
2251
:param a_bzrdir: A BzrDir that contains a branch.
2252
:param name: Name of colocated branch to open, if any
2253
:param _found: a private parameter, do not use it. It is used to
2254
indicate if format probing has already be done.
2255
:param ignore_fallbacks: when set, no fallback branches will be opened
2256
(if there are any). Default is to open fallbacks.
2257
:param location: The location of the referenced branch. If
2258
unspecified, this will be determined from the branch reference in
2260
:param possible_transports: An optional reusable transports list.
2263
name = a_bzrdir._get_selected_branch()
2265
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2266
if format.__class__ != self.__class__:
2267
raise AssertionError("wrong format %r found for %r" %
2269
if location is None:
2270
location = self.get_reference(a_bzrdir, name)
2271
real_bzrdir = controldir.ControlDir.open(
2272
location, possible_transports=possible_transports)
2273
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2274
possible_transports=possible_transports)
2275
# this changes the behaviour of result.clone to create a new reference
2276
# rather than a copy of the content of the branch.
2277
# I did not use a proxy object because that needs much more extensive
2278
# testing, and we are only changing one behaviour at the moment.
2279
# If we decide to alter more behaviours - i.e. the implicit nickname
2280
# then this should be refactored to introduce a tested proxy branch
2281
# and a subclass of that for use in overriding clone() and ....
2283
result.clone = self._make_reference_clone_function(result)
2287
1858
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2288
1859
"""Branch format registry."""
2290
1861
def __init__(self, other_registry=None):
2291
1862
super(BranchFormatRegistry, self).__init__(other_registry)
2292
1863
self._default_format = None
1864
self._default_format_key = None
1866
def get_default(self):
1867
"""Return the current default format."""
1868
if (self._default_format_key is not None
1869
and self._default_format is None):
1870
self._default_format = self.get(self._default_format_key)
1871
return self._default_format
2294
1873
def set_default(self, format):
1874
"""Set the default format."""
2295
1875
self._default_format = format
1876
self._default_format_key = None
2297
def get_default(self):
2298
return self._default_format
1878
def set_default_key(self, format_string):
1879
"""Set the default format by its format string."""
1880
self._default_format_key = format_string
1881
self._default_format = None
2301
1884
network_format_registry = registry.FormatRegistry()
2312
1895
# formats which have no format string are not discoverable
2313
1896
# and not independently creatable, so are not registered.
2314
__format6 = BzrBranchFormat6()
2315
__format7 = BzrBranchFormat7()
2316
__format8 = BzrBranchFormat8()
2317
format_registry.register_lazy(
2318
"Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2319
format_registry.register(BranchReferenceFormat())
2320
format_registry.register(__format6)
2321
format_registry.register(__format7)
2322
format_registry.register(__format8)
2323
format_registry.set_default(__format7)
1897
format_registry.register_lazy(
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1900
format_registry.register_lazy(
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1902
"breezy.bzr.branch", "BzrBranchFormat6")
1903
format_registry.register_lazy(
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1905
"breezy.bzr.branch", "BzrBranchFormat7")
1906
format_registry.register_lazy(
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1908
"breezy.bzr.branch", "BzrBranchFormat8")
1909
format_registry.register_lazy(
1910
b"Bazaar-NG Branch Reference Format 1\n",
1911
"breezy.bzr.branch", "BranchReferenceFormat")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
2326
1916
class BranchWriteLockResult(LogicalLockResult):
2327
1917
"""The result of write locking a branch.
2329
:ivar branch_token: The token obtained from the underlying branch lock, or
1919
:ivar token: The token obtained from the underlying branch lock, or
2331
1921
:ivar unlock: A callable which will unlock the lock.
2334
def __init__(self, unlock, branch_token):
2335
LogicalLockResult.__init__(self, unlock)
2336
self.branch_token = branch_token
2338
1924
def __repr__(self):
2339
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2343
class BzrBranch(Branch, _RelockDebugMixin):
2344
"""A branch stored in the actual filesystem.
2346
Note that it's "local" in the context of the filesystem; it doesn't
2347
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2348
it's writable, and can be accessed via the normal filesystem API.
2350
:ivar _transport: Transport for file operations on this branch's
2351
control files, typically pointing to the .bzr/branch directory.
2352
:ivar repository: Repository for this branch.
2353
:ivar base: The url of the base directory for this branch; the one
2354
containing the .bzr directory.
2355
:ivar name: Optional colocated branch name as it exists in the control
2359
def __init__(self, _format=None,
2360
_control_files=None, a_bzrdir=None, name=None,
2361
_repository=None, ignore_fallbacks=False,
2362
possible_transports=None):
2363
"""Create new branch object at a particular location."""
2364
if a_bzrdir is None:
2365
raise ValueError('a_bzrdir must be supplied')
2367
raise ValueError('name must be supplied')
2368
self.bzrdir = a_bzrdir
2369
self._user_transport = self.bzrdir.transport.clone('..')
2371
self._user_transport.set_segment_parameter(
2372
"branch", urlutils.escape(name))
2373
self._base = self._user_transport.base
2375
self._format = _format
2376
if _control_files is None:
2377
raise ValueError('BzrBranch _control_files is None')
2378
self.control_files = _control_files
2379
self._transport = _control_files._transport
2380
self.repository = _repository
2381
self.conf_store = None
2382
Branch.__init__(self, possible_transports)
2385
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2389
def _get_base(self):
2390
"""Returns the directory containing the control directory."""
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
2399
def _get_config(self):
2400
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2402
def _get_config_store(self):
2403
if self.conf_store is None:
2404
self.conf_store = _mod_config.BranchStore(self)
2405
return self.conf_store
2407
def _uncommitted_branch(self):
2408
"""Return the branch that may contain uncommitted changes."""
2409
master = self.get_master_branch()
2410
if master is not None:
2415
def store_uncommitted(self, creator):
2416
"""Store uncommitted changes from a ShelfCreator.
2418
:param creator: The ShelfCreator containing uncommitted changes, or
2419
None to delete any stored changes.
2420
:raises: ChangesAlreadyStored if the branch already has changes.
2422
branch = self._uncommitted_branch()
2424
branch._transport.delete('stored-transform')
2426
if branch._transport.has('stored-transform'):
2427
raise errors.ChangesAlreadyStored
2428
transform = StringIO()
2429
creator.write_shelf(transform)
2431
branch._transport.put_file('stored-transform', transform)
2433
def get_unshelver(self, tree):
2434
"""Return a shelf.Unshelver for this branch and tree.
2436
:param tree: The tree to use to construct the Unshelver.
2437
:return: an Unshelver or None if no changes are stored.
2439
branch = self._uncommitted_branch()
2441
transform = branch._transport.get('stored-transform')
2442
except errors.NoSuchFile:
2444
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2446
def is_locked(self):
2447
return self.control_files.is_locked()
2449
def lock_write(self, token=None):
2450
"""Lock the branch for write operations.
2452
:param token: A token to permit reacquiring a previously held and
2454
:return: A BranchWriteLockResult.
2456
if not self.is_locked():
2457
self._note_lock('w')
2458
self.repository._warn_if_deprecated(self)
2459
self.repository.lock_write()
2464
return BranchWriteLockResult(self.unlock,
2465
self.control_files.lock_write(token=token))
2468
self.repository.unlock()
2471
def lock_read(self):
2472
"""Lock the branch for read operations.
2474
:return: A bzrlib.lock.LogicalLockResult.
2476
if not self.is_locked():
2477
self._note_lock('r')
2478
self.repository._warn_if_deprecated(self)
2479
self.repository.lock_read()
2484
self.control_files.lock_read()
2485
return LogicalLockResult(self.unlock)
2488
self.repository.unlock()
2491
@only_raises(errors.LockNotHeld, errors.LockBroken)
2493
if self.control_files._lock_count == 1 and self.conf_store is not None:
2494
self.conf_store.save_changes()
2496
self.control_files.unlock()
2498
if not self.control_files.is_locked():
2499
self.repository.unlock()
2500
# we just released the lock
2501
self._clear_cached_state()
2503
def peek_lock_mode(self):
2504
if self.control_files._lock_count == 0:
2507
return self.control_files._lock_mode
2509
def get_physical_lock_status(self):
2510
return self.control_files.get_physical_lock_status()
2513
def print_file(self, file, revision_id):
2514
"""See Branch.print_file."""
2515
return self.repository.print_file(file, revision_id)
2518
def set_last_revision_info(self, revno, revision_id):
2519
if not revision_id or not isinstance(revision_id, basestring):
2520
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2521
revision_id = _mod_revision.ensure_null(revision_id)
2522
old_revno, old_revid = self.last_revision_info()
2523
if self.get_append_revisions_only():
2524
self._check_history_violation(revision_id)
2525
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2526
self._write_last_revision_info(revno, revision_id)
2527
self._clear_cached_state()
2528
self._last_revision_info_cache = revno, revision_id
2529
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2531
def basis_tree(self):
2532
"""See Branch.basis_tree."""
2533
return self.repository.revision_tree(self.last_revision())
2535
def _get_parent_location(self):
2536
_locs = ['parent', 'pull', 'x-pull']
2539
return self._transport.get_bytes(l).strip('\n')
2540
except errors.NoSuchFile:
2544
def get_stacked_on_url(self):
2545
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2547
def set_push_location(self, location):
2548
"""See Branch.set_push_location."""
2549
self.get_config().set_user_option(
2550
'push_location', location,
2551
store=_mod_config.STORE_LOCATION_NORECURSE)
2553
def _set_parent_location(self, url):
2555
self._transport.delete('parent')
2557
self._transport.put_bytes('parent', url + '\n',
2558
mode=self.bzrdir._get_file_mode())
2562
"""If bound, unbind"""
2563
return self.set_bound_location(None)
2566
def bind(self, other):
2567
"""Bind this branch to the branch other.
2569
This does not push or pull data between the branches, though it does
2570
check for divergence to raise an error when the branches are not
2571
either the same, or one a prefix of the other. That behaviour may not
2572
be useful, so that check may be removed in future.
2574
:param other: The branch to bind to
2577
# TODO: jam 20051230 Consider checking if the target is bound
2578
# It is debatable whether you should be able to bind to
2579
# a branch which is itself bound.
2580
# Committing is obviously forbidden,
2581
# but binding itself may not be.
2582
# Since we *have* to check at commit time, we don't
2583
# *need* to check here
2585
# we want to raise diverged if:
2586
# last_rev is not in the other_last_rev history, AND
2587
# other_last_rev is not in our history, and do it without pulling
2589
self.set_bound_location(other.base)
2591
def get_bound_location(self):
2593
return self._transport.get_bytes('bound')[:-1]
2594
except errors.NoSuchFile:
2598
def get_master_branch(self, possible_transports=None):
2599
"""Return the branch we are bound to.
2601
:return: Either a Branch, or None
2603
if self._master_branch_cache is None:
2604
self._master_branch_cache = self._get_master_branch(
2605
possible_transports)
2606
return self._master_branch_cache
2608
def _get_master_branch(self, possible_transports):
2609
bound_loc = self.get_bound_location()
2613
return Branch.open(bound_loc,
2614
possible_transports=possible_transports)
2615
except (errors.NotBranchError, errors.ConnectionError), e:
2616
raise errors.BoundBranchConnectionFailure(
2620
def set_bound_location(self, location):
2621
"""Set the target where this branch is bound to.
2623
:param location: URL to the target branch
2625
self._master_branch_cache = None
2627
self._transport.put_bytes('bound', location+'\n',
2628
mode=self.bzrdir._get_file_mode())
2631
self._transport.delete('bound')
2632
except errors.NoSuchFile:
2637
def update(self, possible_transports=None):
2638
"""Synchronise this branch with the master branch if any.
2640
:return: None or the last_revision that was pivoted out during the
2643
master = self.get_master_branch(possible_transports)
2644
if master is not None:
2645
old_tip = _mod_revision.ensure_null(self.last_revision())
2646
self.pull(master, overwrite=True)
2647
if self.repository.get_graph().is_ancestor(old_tip,
2648
_mod_revision.ensure_null(self.last_revision())):
2653
def _read_last_revision_info(self):
2654
revision_string = self._transport.get_bytes('last-revision')
2655
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2656
revision_id = cache_utf8.get_cached_utf8(revision_id)
2658
return revno, revision_id
2660
def _write_last_revision_info(self, revno, revision_id):
2661
"""Simply write out the revision id, with no checks.
2663
Use set_last_revision_info to perform this safely.
2665
Does not update the revision_history cache.
2667
revision_id = _mod_revision.ensure_null(revision_id)
2668
out_string = '%d %s\n' % (revno, revision_id)
2669
self._transport.put_bytes('last-revision', out_string,
2670
mode=self.bzrdir._get_file_mode())
2673
def update_feature_flags(self, updated_flags):
2674
"""Update the feature flags for this branch.
2676
:param updated_flags: Dictionary mapping feature names to necessities
2677
A necessity can be None to indicate the feature should be removed
2679
self._format._update_feature_flags(updated_flags)
2680
self.control_transport.put_bytes('format', self._format.as_string())
2683
class BzrBranch8(BzrBranch):
2684
"""A branch that stores tree-reference locations."""
2686
def _open_hook(self, possible_transports=None):
2687
if self._ignore_fallbacks:
2689
if possible_transports is None:
2690
possible_transports = [self.bzrdir.root_transport]
2692
url = self.get_stacked_on_url()
2693
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2694
errors.UnstackableBranchFormat):
2697
for hook in Branch.hooks['transform_fallback_location']:
2698
url = hook(self, url)
2700
hook_name = Branch.hooks.get_hook_name(hook)
2701
raise AssertionError(
2702
"'transform_fallback_location' hook %s returned "
2703
"None, not a URL." % hook_name)
2704
self._activate_fallback_location(url,
2705
possible_transports=possible_transports)
2707
def __init__(self, *args, **kwargs):
2708
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2709
super(BzrBranch8, self).__init__(*args, **kwargs)
2710
self._last_revision_info_cache = None
2711
self._reference_info = None
2713
def _clear_cached_state(self):
2714
super(BzrBranch8, self)._clear_cached_state()
2715
self._last_revision_info_cache = None
2716
self._reference_info = None
2718
def _check_history_violation(self, revision_id):
2719
current_revid = self.last_revision()
2720
last_revision = _mod_revision.ensure_null(current_revid)
2721
if _mod_revision.is_null(last_revision):
2723
graph = self.repository.get_graph()
2724
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2725
if lh_ancestor == current_revid:
2727
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2729
def _gen_revision_history(self):
2730
"""Generate the revision history from last revision
2732
last_revno, last_revision = self.last_revision_info()
2733
self._extend_partial_history(stop_index=last_revno-1)
2734
return list(reversed(self._partial_revision_history_cache))
2737
def _set_parent_location(self, url):
2738
"""Set the parent branch"""
2739
self._set_config_location('parent_location', url, make_relative=True)
2742
def _get_parent_location(self):
2743
"""Set the parent branch"""
2744
return self._get_config_location('parent_location')
2747
def _set_all_reference_info(self, info_dict):
2748
"""Replace all reference info stored in a branch.
2750
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2753
writer = rio.RioWriter(s)
2754
for key, (tree_path, branch_location) in info_dict.iteritems():
2755
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2756
branch_location=branch_location)
2757
writer.write_stanza(stanza)
2758
self._transport.put_bytes('references', s.getvalue())
2759
self._reference_info = info_dict
2762
def _get_all_reference_info(self):
2763
"""Return all the reference info stored in a branch.
2765
:return: A dict of {file_id: (tree_path, branch_location)}
2767
if self._reference_info is not None:
2768
return self._reference_info
2769
rio_file = self._transport.get('references')
2771
stanzas = rio.read_stanzas(rio_file)
2772
info_dict = dict((s['file_id'], (s['tree_path'],
2773
s['branch_location'])) for s in stanzas)
2776
self._reference_info = info_dict
2779
def set_reference_info(self, file_id, tree_path, branch_location):
2780
"""Set the branch location to use for a tree reference.
2782
:param file_id: The file-id of the tree reference.
2783
:param tree_path: The path of the tree reference in the tree.
2784
:param branch_location: The location of the branch to retrieve tree
2787
info_dict = self._get_all_reference_info()
2788
info_dict[file_id] = (tree_path, branch_location)
2789
if None in (tree_path, branch_location):
2790
if tree_path is not None:
2791
raise ValueError('tree_path must be None when branch_location'
2793
if branch_location is not None:
2794
raise ValueError('branch_location must be None when tree_path'
2796
del info_dict[file_id]
2797
self._set_all_reference_info(info_dict)
2799
def get_reference_info(self, file_id):
2800
"""Get the tree_path and branch_location for a tree reference.
2802
:return: a tuple of (tree_path, branch_location)
2804
return self._get_all_reference_info().get(file_id, (None, None))
2806
def reference_parent(self, file_id, path, possible_transports=None):
2807
"""Return the parent branch for a tree-reference file_id.
2809
:param file_id: The file_id of the tree reference
2810
:param path: The path of the file_id in the tree
2811
:return: A branch associated with the file_id
2813
branch_location = self.get_reference_info(file_id)[1]
2814
if branch_location is None:
2815
return Branch.reference_parent(self, file_id, path,
2816
possible_transports)
2817
branch_location = urlutils.join(self.user_url, branch_location)
2818
return Branch.open(branch_location,
2819
possible_transports=possible_transports)
2821
def set_push_location(self, location):
2822
"""See Branch.set_push_location."""
2823
self._set_config_location('push_location', location)
2825
def set_bound_location(self, location):
2826
"""See Branch.set_push_location."""
2827
self._master_branch_cache = None
2829
conf = self.get_config_stack()
2830
if location is None:
2831
if not conf.get('bound'):
2834
conf.set('bound', 'False')
2837
self._set_config_location('bound_location', location,
2839
conf.set('bound', 'True')
2842
def _get_bound_location(self, bound):
2843
"""Return the bound location in the config file.
2845
Return None if the bound parameter does not match"""
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
2849
return self._get_config_location('bound_location', config=conf)
2851
def get_bound_location(self):
2852
"""See Branch.get_bound_location."""
2853
return self._get_bound_location(True)
2855
def get_old_bound_location(self):
2856
"""See Branch.get_old_bound_location"""
2857
return self._get_bound_location(False)
2859
def get_stacked_on_url(self):
2860
# you can always ask for the URL; but you might not be able to use it
2861
# if the repo can't support stacking.
2862
## self._check_stackable_repo()
2863
# stacked_on_location is only ever defined in branch.conf, so don't
2864
# waste effort reading the whole stack of config files.
2865
conf = _mod_config.BranchOnlyStack(self)
2866
stacked_url = self._get_config_location('stacked_on_location',
2868
if stacked_url is None:
2869
raise errors.NotStacked(self)
2870
return stacked_url.encode('utf-8')
2873
def get_rev_id(self, revno, history=None):
2874
"""Find the revision id of the specified revno."""
2876
return _mod_revision.NULL_REVISION
2878
last_revno, last_revision_id = self.last_revision_info()
2879
if revno <= 0 or revno > last_revno:
2880
raise errors.NoSuchRevision(self, revno)
2882
if history is not None:
2883
return history[revno - 1]
2885
index = last_revno - revno
2886
if len(self._partial_revision_history_cache) <= index:
2887
self._extend_partial_history(stop_index=index)
2888
if len(self._partial_revision_history_cache) > index:
2889
return self._partial_revision_history_cache[index]
2891
raise errors.NoSuchRevision(self, revno)
2894
def revision_id_to_revno(self, revision_id):
2895
"""Given a revision id, return its revno"""
2896
if _mod_revision.is_null(revision_id):
2899
index = self._partial_revision_history_cache.index(revision_id)
2902
self._extend_partial_history(stop_revision=revision_id)
2903
except errors.RevisionNotPresent, e:
2904
raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2905
index = len(self._partial_revision_history_cache) - 1
2907
raise errors.NoSuchRevision(self, revision_id)
2908
if self._partial_revision_history_cache[index] != revision_id:
2909
raise errors.NoSuchRevision(self, revision_id)
2910
return self.revno() - index
2913
class BzrBranch7(BzrBranch8):
2914
"""A branch with support for a fallback repository."""
2916
def set_reference_info(self, file_id, tree_path, branch_location):
2917
Branch.set_reference_info(self, file_id, tree_path, branch_location)
2919
def get_reference_info(self, file_id):
2920
Branch.get_reference_info(self, file_id)
2922
def reference_parent(self, file_id, path, possible_transports=None):
2923
return Branch.reference_parent(self, file_id, path,
2924
possible_transports)
2927
class BzrBranch6(BzrBranch7):
2928
"""See BzrBranchFormat6 for the capabilities of this branch.
2930
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2934
def get_stacked_on_url(self):
2935
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1925
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
2938
1928
######################################################################
3189
2131
return format._custom_format
3193
def copy_content_into(self, revision_id=None):
2134
def copy_content_into(self, revision_id=None, tag_selector=None):
3194
2135
"""Copy the content of source into target
3196
2137
revision_id: if not None, the revision history in the new branch will
3197
2138
be truncated to end with revision_id.
3199
self.source.update_references(self.target)
3200
self.source._synchronize_history(self.target, revision_id)
3202
parent = self.source.get_parent()
3203
except errors.InaccessibleParent, e:
3204
mutter('parent was not accessible to copy: %s', e)
3207
self.target.set_parent(parent)
3208
if self.source._push_should_merge_tags():
3209
self.source.tags.merge_to(self.target.tags)
2140
with self.source.lock_read(), self.target.lock_write():
2141
self.source._synchronize_history(self.target, revision_id)
2142
self.update_references()
2144
parent = self.source.get_parent()
2145
except errors.InaccessibleParent as e:
2146
mutter('parent was not accessible to copy: %s', str(e))
2149
self.target.set_parent(parent)
2150
if self.source._push_should_merge_tags():
2151
self.source.tags.merge_to(self.target.tags, selector=tag_selector)
3212
def fetch(self, stop_revision=None, limit=None):
2153
def fetch(self, stop_revision=None, limit=None, lossy=False):
3213
2154
if self.target.base == self.source.base:
3215
self.source.lock_read()
2156
with self.source.lock_read(), self.target.lock_write():
3217
2157
fetch_spec_factory = fetch.FetchSpecFactory()
3218
2158
fetch_spec_factory.source_branch = self.source
3219
2159
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3220
2160
fetch_spec_factory.source_repo = self.source.repository
3221
2161
fetch_spec_factory.target_repo = self.target.repository
3222
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
2162
fetch_spec_factory.target_repo_kind = (
2163
fetch.TargetRepoKinds.PREEXISTING)
3223
2164
fetch_spec_factory.limit = limit
3224
2165
fetch_spec = fetch_spec_factory.make_fetch_spec()
3225
return self.target.repository.fetch(self.source.repository,
2166
return self.target.repository.fetch(
2167
self.source.repository,
3226
2169
fetch_spec=fetch_spec)
3228
self.source.unlock()
3231
2171
def _update_revisions(self, stop_revision=None, overwrite=False,
3233
other_revno, other_last_revision = self.source.last_revision_info()
3234
stop_revno = None # unknown
3235
if stop_revision is None:
3236
stop_revision = other_last_revision
3237
if _mod_revision.is_null(stop_revision):
3238
# if there are no commits, we're done.
3240
stop_revno = other_revno
2173
with self.source.lock_read(), self.target.lock_write():
2174
other_revno, other_last_revision = self.source.last_revision_info()
2175
stop_revno = None # unknown
2176
if stop_revision is None:
2177
stop_revision = other_last_revision
2178
if _mod_revision.is_null(stop_revision):
2179
# if there are no commits, we're done.
2181
stop_revno = other_revno
3242
# what's the current last revision, before we fetch [and change it
3244
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3245
# we fetch here so that we don't process data twice in the common
3246
# case of having something to pull, and so that the check for
3247
# already merged can operate on the just fetched graph, which will
3248
# be cached in memory.
3249
self.fetch(stop_revision=stop_revision)
3250
# Check to see if one is an ancestor of the other
3253
graph = self.target.repository.get_graph()
3254
if self.target._check_if_descendant_or_diverged(
3255
stop_revision, last_rev, graph, self.source):
3256
# stop_revision is a descendant of last_rev, but we aren't
3257
# overwriting, so we're done.
3259
if stop_revno is None:
3261
graph = self.target.repository.get_graph()
3262
this_revno, this_last_revision = \
2183
# what's the current last revision, before we fetch [and change it
2185
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2186
# we fetch here so that we don't process data twice in the common
2187
# case of having something to pull, and so that the check for
2188
# already merged can operate on the just fetched graph, which will
2189
# be cached in memory.
2190
self.fetch(stop_revision=stop_revision)
2191
# Check to see if one is an ancestor of the other
2194
graph = self.target.repository.get_graph()
2195
if self.target._check_if_descendant_or_diverged(
2196
stop_revision, last_rev, graph, self.source):
2197
# stop_revision is a descendant of last_rev, but we aren't
2198
# overwriting, so we're done.
2200
if stop_revno is None:
2202
graph = self.target.repository.get_graph()
2203
this_revno, this_last_revision = \
3263
2204
self.target.last_revision_info()
3264
stop_revno = graph.find_distance_to_null(stop_revision,
3265
[(other_last_revision, other_revno),
3266
(this_last_revision, this_revno)])
3267
self.target.set_last_revision_info(stop_revno, stop_revision)
2205
stop_revno = graph.find_distance_to_null(
2206
stop_revision, [(other_last_revision, other_revno),
2207
(this_last_revision, this_revno)])
2208
self.target.set_last_revision_info(stop_revno, stop_revision)
3270
2210
def pull(self, overwrite=False, stop_revision=None,
3271
2211
possible_transports=None, run_hooks=True,
3272
_override_hook_target=None, local=False):
2212
_override_hook_target=None, local=False,
3273
2214
"""Pull from source into self, updating my master if any.
3275
2216
:param run_hooks: Private parameter - if false, this branch
3276
2217
is being called because it's the master of the primary branch,
3277
2218
so it should not run its hooks.
3279
bound_location = self.target.get_bound_location()
3280
if local and not bound_location:
3281
raise errors.LocalRequiresBoundBranch()
3282
master_branch = None
3283
source_is_master = False
3285
# bound_location comes from a config file, some care has to be
3286
# taken to relate it to source.user_url
3287
normalized = urlutils.normalize_url(bound_location)
3289
relpath = self.source.user_transport.relpath(normalized)
3290
source_is_master = (relpath == '')
3291
except (errors.PathNotChild, errors.InvalidURL):
3292
source_is_master = False
3293
if not local and bound_location and not source_is_master:
3294
# not pulling from master, so we need to update master.
3295
master_branch = self.target.get_master_branch(possible_transports)
3296
master_branch.lock_write()
2220
with contextlib.ExitStack() as exit_stack:
2221
exit_stack.enter_context(self.target.lock_write())
2222
bound_location = self.target.get_bound_location()
2223
if local and not bound_location:
2224
raise errors.LocalRequiresBoundBranch()
2225
master_branch = None
2226
source_is_master = False
2228
# bound_location comes from a config file, some care has to be
2229
# taken to relate it to source.user_url
2230
normalized = urlutils.normalize_url(bound_location)
2232
relpath = self.source.user_transport.relpath(normalized)
2233
source_is_master = (relpath == '')
2234
except (errors.PathNotChild, urlutils.InvalidURL):
2235
source_is_master = False
2236
if not local and bound_location and not source_is_master:
2237
# not pulling from master, so we need to update master.
2238
master_branch = self.target.get_master_branch(
2239
possible_transports)
2240
exit_stack.enter_context(master_branch.lock_write())
3298
2241
if master_branch:
3299
2242
# pull from source into master.
3300
master_branch.pull(self.source, overwrite, stop_revision,
3302
return self._pull(overwrite,
3303
stop_revision, _hook_master=master_branch,
2244
self.source, overwrite, stop_revision, run_hooks=False,
2245
tag_selector=tag_selector)
2247
overwrite, stop_revision, _hook_master=master_branch,
3304
2248
run_hooks=run_hooks,
3305
2249
_override_hook_target=_override_hook_target,
3306
merge_tags_to_master=not source_is_master)
3309
master_branch.unlock()
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
3311
2253
def push(self, overwrite=False, stop_revision=None, lossy=False,
3312
_override_hook_source_branch=None):
2254
_override_hook_source_branch=None, tag_selector=None):
3313
2255
"""See InterBranch.push.
3315
2257
This is the basic concrete implementation of push()