1502
1561
object will be created every time regardless.
1505
_default_format = None
1506
"""The default format used for new branches."""
1509
"""The known formats."""
1511
can_set_append_revisions_only = True
1513
1564
def __eq__(self, other):
1514
1565
return self.__class__ is other.__class__
1516
1567
def __ne__(self, other):
1517
1568
return not (self == other)
1520
def find_format(klass, a_bzrdir, name=None):
1521
"""Return the format for the branch object in a_bzrdir."""
1523
transport = a_bzrdir.get_branch_transport(None, name=name)
1524
format_string = transport.get_bytes("format")
1525
return klass._formats[format_string]
1526
except errors.NoSuchFile:
1527
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1529
raise errors.UnknownFormatError(format=format_string, kind='branch')
1532
def get_default_format(klass):
1533
"""Return the current default format."""
1534
return klass._default_format
1536
def get_reference(self, a_bzrdir, name=None):
1537
"""Get the target reference of the branch in a_bzrdir.
1570
def get_reference(self, controldir, name=None):
1571
"""Get the target reference of the branch in controldir.
1539
1573
format probing must have been completed before calling
1540
1574
this method - it is assumed that the format of the branch
1541
in a_bzrdir is correct.
1575
in controldir is correct.
1543
:param a_bzrdir: The bzrdir to get the branch data from.
1577
:param controldir: The controldir to get the branch data from.
1544
1578
:param name: Name of the colocated branch to fetch
1545
1579
:return: None if the branch is not a reference branch.
1550
def set_reference(self, a_bzrdir, name, to_branch):
1551
"""Set the target reference of the branch in a_bzrdir.
1584
def set_reference(self, controldir, name, to_branch):
1585
"""Set the target reference of the branch in controldir.
1553
1587
format probing must have been completed before calling
1554
1588
this method - it is assumed that the format of the branch
1555
in a_bzrdir is correct.
1589
in controldir is correct.
1557
:param a_bzrdir: The bzrdir to set the branch reference for.
1591
:param controldir: The controldir to set the branch reference for.
1558
1592
:param name: Name of colocated branch to set, None for default
1559
1593
:param to_branch: branch that the checkout is to reference
1561
1595
raise NotImplementedError(self.set_reference)
1563
def get_format_string(self):
1564
"""Return the ASCII format string that identifies this format."""
1565
raise NotImplementedError(self.get_format_string)
1567
1597
def get_format_description(self):
1568
1598
"""Return the short format description for this format."""
1569
1599
raise NotImplementedError(self.get_format_description)
1571
def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1601
def _run_post_branch_init_hooks(self, controldir, name, branch):
1572
1602
hooks = Branch.hooks['post_branch_init']
1575
params = BranchInitHookParams(self, a_bzrdir, name, branch)
1605
params = BranchInitHookParams(self, controldir, name, branch)
1576
1606
for hook in hooks:
1579
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1580
lock_type='metadir', set_format=True):
1581
"""Initialize a branch in a bzrdir, with specified files
1583
:param a_bzrdir: The bzrdir to initialize the branch in
1584
:param utf8_files: The files to create as a list of
1585
(filename, content) tuples
1586
:param name: Name of colocated branch to create, if any
1587
:param set_format: If True, set the format with
1588
self.get_format_string. (BzrBranch4 has its format set
1590
:return: a branch in this format
1592
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1593
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1595
'metadir': ('lock', lockdir.LockDir),
1596
'branch4': ('branch-lock', lockable_files.TransportLock),
1598
lock_name, lock_class = lock_map[lock_type]
1599
control_files = lockable_files.LockableFiles(branch_transport,
1600
lock_name, lock_class)
1601
control_files.create_lock()
1603
control_files.lock_write()
1604
except errors.LockContention:
1605
if lock_type != 'branch4':
1611
utf8_files += [('format', self.get_format_string())]
1613
for (filename, content) in utf8_files:
1614
branch_transport.put_bytes(
1616
mode=a_bzrdir._get_file_mode())
1619
control_files.unlock()
1620
branch = self.open(a_bzrdir, name, _found=True)
1621
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1624
def initialize(self, a_bzrdir, name=None):
1625
"""Create a branch of this format in a_bzrdir.
1609
def initialize(self, controldir, name=None, repository=None,
1610
append_revisions_only=None):
1611
"""Create a branch of this format in controldir.
1627
1613
:param name: Name of the colocated branch to create.
1629
1615
raise NotImplementedError(self.initialize)
1918
1890
self.revision_id)
1921
class BzrBranchFormat4(BranchFormat):
1922
"""Bzr branch format 4.
1925
- a revision-history file.
1926
- a branch-lock lock file [ to be shared with the bzrdir ]
1929
def get_format_description(self):
1930
"""See BranchFormat.get_format_description()."""
1931
return "Branch format 4"
1933
def initialize(self, a_bzrdir, name=None):
1934
"""Create a branch of this format in a_bzrdir."""
1935
utf8_files = [('revision-history', ''),
1936
('branch-name', ''),
1938
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1939
lock_type='branch4', set_format=False)
1942
super(BzrBranchFormat4, self).__init__()
1943
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1945
def network_name(self):
1946
"""The network name for this format is the control dirs disk label."""
1947
return self._matchingbzrdir.get_format_string()
1949
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1950
"""See BranchFormat.open()."""
1952
# we are being called directly and must probe.
1953
raise NotImplementedError
1954
return BzrBranch(_format=self,
1955
_control_files=a_bzrdir._control_files,
1958
_repository=a_bzrdir.open_repository())
1961
return "Bazaar-NG branch format 4"
1964
class BranchFormatMetadir(BranchFormat):
1965
"""Common logic for meta-dir based branch formats."""
1967
def _branch_class(self):
1968
"""What class to instantiate on open calls."""
1969
raise NotImplementedError(self._branch_class)
1971
def network_name(self):
1972
"""A simple byte string uniquely identifying this format for RPC calls.
1974
Metadir branch formats use their format string.
1976
return self.get_format_string()
1978
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1979
"""See BranchFormat.open()."""
1981
format = BranchFormat.find_format(a_bzrdir, name=name)
1982
if format.__class__ != self.__class__:
1983
raise AssertionError("wrong format %r found for %r" %
1985
transport = a_bzrdir.get_branch_transport(None, name=name)
1987
control_files = lockable_files.LockableFiles(transport, 'lock',
1989
return self._branch_class()(_format=self,
1990
_control_files=control_files,
1993
_repository=a_bzrdir.find_repository(),
1994
ignore_fallbacks=ignore_fallbacks)
1995
except errors.NoSuchFile:
1996
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1999
super(BranchFormatMetadir, self).__init__()
2000
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2001
self._matchingbzrdir.set_branch_format(self)
2003
def supports_tags(self):
2007
class BzrBranchFormat5(BranchFormatMetadir):
2008
"""Bzr branch format 5.
2011
- a revision-history file.
2013
- a lock dir guarding the branch itself
2014
- all of this stored in a branch/ subdirectory
2015
- works with shared repositories.
2017
This format is new in bzr 0.8.
2020
def _branch_class(self):
2023
def get_format_string(self):
2024
"""See BranchFormat.get_format_string()."""
2025
return "Bazaar-NG branch format 5\n"
2027
def get_format_description(self):
2028
"""See BranchFormat.get_format_description()."""
2029
return "Branch format 5"
2031
def initialize(self, a_bzrdir, name=None):
2032
"""Create a branch of this format in a_bzrdir."""
2033
utf8_files = [('revision-history', ''),
2034
('branch-name', ''),
2036
return self._initialize_helper(a_bzrdir, utf8_files, name)
2038
def supports_tags(self):
2042
class BzrBranchFormat6(BranchFormatMetadir):
2043
"""Branch format with last-revision and tags.
2045
Unlike previous formats, this has no explicit revision history. Instead,
2046
this just stores the last-revision, and the left-hand history leading
2047
up to there is the history.
2049
This format was introduced in bzr 0.15
2050
and became the default in 0.91.
2053
def _branch_class(self):
2056
def get_format_string(self):
2057
"""See BranchFormat.get_format_string()."""
2058
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2060
def get_format_description(self):
2061
"""See BranchFormat.get_format_description()."""
2062
return "Branch format 6"
2064
def initialize(self, a_bzrdir, name=None):
2065
"""Create a branch of this format in a_bzrdir."""
2066
utf8_files = [('last-revision', '0 null:\n'),
2067
('branch.conf', ''),
2070
return self._initialize_helper(a_bzrdir, utf8_files, name)
2072
def make_tags(self, branch):
2073
"""See bzrlib.branch.BranchFormat.make_tags()."""
2074
return BasicTags(branch)
2076
def supports_set_append_revisions_only(self):
2080
class BzrBranchFormat8(BranchFormatMetadir):
2081
"""Metadir format supporting storing locations of subtree branches."""
2083
def _branch_class(self):
2086
def get_format_string(self):
2087
"""See BranchFormat.get_format_string()."""
2088
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2090
def get_format_description(self):
2091
"""See BranchFormat.get_format_description()."""
2092
return "Branch format 8"
2094
def initialize(self, a_bzrdir, name=None):
2095
"""Create a branch of this format in a_bzrdir."""
2096
utf8_files = [('last-revision', '0 null:\n'),
2097
('branch.conf', ''),
2101
return self._initialize_helper(a_bzrdir, utf8_files, name)
2104
super(BzrBranchFormat8, self).__init__()
2105
self._matchingbzrdir.repository_format = \
2106
RepositoryFormatKnitPack5RichRoot()
2108
def make_tags(self, branch):
2109
"""See bzrlib.branch.BranchFormat.make_tags()."""
2110
return BasicTags(branch)
2112
def supports_set_append_revisions_only(self):
2115
def supports_stacking(self):
2118
supports_reference_locations = True
2121
class BzrBranchFormat7(BzrBranchFormat8):
2122
"""Branch format with last-revision, tags, and a stacked location pointer.
2124
The stacked location pointer is passed down to the repository and requires
2125
a repository format with supports_external_lookups = True.
2127
This format was introduced in bzr 1.6.
2130
def initialize(self, a_bzrdir, name=None):
2131
"""Create a branch of this format in a_bzrdir."""
2132
utf8_files = [('last-revision', '0 null:\n'),
2133
('branch.conf', ''),
2136
return self._initialize_helper(a_bzrdir, utf8_files, name)
2138
def _branch_class(self):
2141
def get_format_string(self):
2142
"""See BranchFormat.get_format_string()."""
2143
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2145
def get_format_description(self):
2146
"""See BranchFormat.get_format_description()."""
2147
return "Branch format 7"
2149
def supports_set_append_revisions_only(self):
2152
supports_reference_locations = False
2155
class BranchReferenceFormat(BranchFormat):
2156
"""Bzr branch reference format.
2158
Branch references are used in implementing checkouts, they
2159
act as an alias to the real branch which is at some other url.
2166
def get_format_string(self):
2167
"""See BranchFormat.get_format_string()."""
2168
return "Bazaar-NG Branch Reference Format 1\n"
2170
def get_format_description(self):
2171
"""See BranchFormat.get_format_description()."""
2172
return "Checkout reference format 1"
2174
def get_reference(self, a_bzrdir, name=None):
2175
"""See BranchFormat.get_reference()."""
2176
transport = a_bzrdir.get_branch_transport(None, name=name)
2177
return transport.get_bytes('location')
2179
def set_reference(self, a_bzrdir, name, to_branch):
2180
"""See BranchFormat.set_reference()."""
2181
transport = a_bzrdir.get_branch_transport(None, name=name)
2182
location = transport.put_bytes('location', to_branch.base)
2184
def initialize(self, a_bzrdir, name=None, target_branch=None):
2185
"""Create a branch of this format in a_bzrdir."""
2186
if target_branch is None:
2187
# this format does not implement branch itself, thus the implicit
2188
# creation contract must see it as uninitializable
2189
raise errors.UninitializableFormat(self)
2190
mutter('creating branch reference in %s', a_bzrdir.user_url)
2191
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2192
branch_transport.put_bytes('location',
2193
target_branch.bzrdir.user_url)
2194
branch_transport.put_bytes('format', self.get_format_string())
2196
a_bzrdir, name, _found=True,
2197
possible_transports=[target_branch.bzrdir.root_transport])
2198
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2202
super(BranchReferenceFormat, self).__init__()
2203
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2204
self._matchingbzrdir.set_branch_format(self)
2206
def _make_reference_clone_function(format, a_branch):
2207
"""Create a clone() routine for a branch dynamically."""
2208
def clone(to_bzrdir, revision_id=None,
2209
repository_policy=None):
2210
"""See Branch.clone()."""
2211
return format.initialize(to_bzrdir, target_branch=a_branch)
2212
# cannot obey revision_id limits when cloning a reference ...
2213
# FIXME RBC 20060210 either nuke revision_id for clone, or
2214
# emit some sort of warning/error to the caller ?!
2217
def open(self, a_bzrdir, name=None, _found=False, location=None,
2218
possible_transports=None, ignore_fallbacks=False):
2219
"""Return the branch that the branch reference in a_bzrdir points at.
2221
:param a_bzrdir: A BzrDir that contains a branch.
2222
:param name: Name of colocated branch to open, if any
2223
:param _found: a private parameter, do not use it. It is used to
2224
indicate if format probing has already be done.
2225
:param ignore_fallbacks: when set, no fallback branches will be opened
2226
(if there are any). Default is to open fallbacks.
2227
:param location: The location of the referenced branch. If
2228
unspecified, this will be determined from the branch reference in
2230
:param possible_transports: An optional reusable transports list.
2233
format = BranchFormat.find_format(a_bzrdir, name=name)
2234
if format.__class__ != self.__class__:
2235
raise AssertionError("wrong format %r found for %r" %
2237
if location is None:
2238
location = self.get_reference(a_bzrdir, name)
2239
real_bzrdir = bzrdir.BzrDir.open(
2240
location, possible_transports=possible_transports)
2241
result = real_bzrdir.open_branch(name=name,
2242
ignore_fallbacks=ignore_fallbacks)
2243
# this changes the behaviour of result.clone to create a new reference
2244
# rather than a copy of the content of the branch.
2245
# I did not use a proxy object because that needs much more extensive
2246
# testing, and we are only changing one behaviour at the moment.
2247
# If we decide to alter more behaviours - i.e. the implicit nickname
2248
# then this should be refactored to introduce a tested proxy branch
2249
# and a subclass of that for use in overriding clone() and ....
2251
result.clone = self._make_reference_clone_function(result)
1893
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1894
"""Branch format registry."""
1896
def __init__(self, other_registry=None):
1897
super(BranchFormatRegistry, self).__init__(other_registry)
1898
self._default_format = None
1899
self._default_format_key = None
1901
def get_default(self):
1902
"""Return the current default format."""
1903
if (self._default_format_key is not None and
1904
self._default_format is None):
1905
self._default_format = self.get(self._default_format_key)
1906
return self._default_format
1908
def set_default(self, format):
1909
"""Set the default format."""
1910
self._default_format = format
1911
self._default_format_key = None
1913
def set_default_key(self, format_string):
1914
"""Set the default format by its format string."""
1915
self._default_format_key = format_string
1916
self._default_format = None
2255
1919
network_format_registry = registry.FormatRegistry()
2299
class BzrBranch(Branch, _RelockDebugMixin):
2300
"""A branch stored in the actual filesystem.
2302
Note that it's "local" in the context of the filesystem; it doesn't
2303
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2304
it's writable, and can be accessed via the normal filesystem API.
2306
:ivar _transport: Transport for file operations on this branch's
2307
control files, typically pointing to the .bzr/branch directory.
2308
:ivar repository: Repository for this branch.
2309
:ivar base: The url of the base directory for this branch; the one
2310
containing the .bzr directory.
2311
:ivar name: Optional colocated branch name as it exists in the control
2315
def __init__(self, _format=None,
2316
_control_files=None, a_bzrdir=None, name=None,
2317
_repository=None, ignore_fallbacks=False):
2318
"""Create new branch object at a particular location."""
2319
if a_bzrdir is None:
2320
raise ValueError('a_bzrdir must be supplied')
2322
self.bzrdir = a_bzrdir
2323
self._base = self.bzrdir.transport.clone('..').base
2325
# XXX: We should be able to just do
2326
# self.base = self.bzrdir.root_transport.base
2327
# but this does not quite work yet -- mbp 20080522
2328
self._format = _format
2329
if _control_files is None:
2330
raise ValueError('BzrBranch _control_files is None')
2331
self.control_files = _control_files
2332
self._transport = _control_files._transport
2333
self.repository = _repository
2334
Branch.__init__(self)
2337
if self.name is None:
2338
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2340
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2345
def _get_base(self):
2346
"""Returns the directory containing the control directory."""
2349
base = property(_get_base, doc="The URL for the root of this branch.")
2351
def _get_config(self):
2352
return TransportConfig(self._transport, 'branch.conf')
2354
def is_locked(self):
2355
return self.control_files.is_locked()
2357
def lock_write(self, token=None):
2358
"""Lock the branch for write operations.
2360
:param token: A token to permit reacquiring a previously held and
2362
:return: A BranchWriteLockResult.
2364
if not self.is_locked():
2365
self._note_lock('w')
2366
# All-in-one needs to always unlock/lock.
2367
repo_control = getattr(self.repository, 'control_files', None)
2368
if self.control_files == repo_control or not self.is_locked():
2369
self.repository._warn_if_deprecated(self)
2370
self.repository.lock_write()
2375
return BranchWriteLockResult(self.unlock,
2376
self.control_files.lock_write(token=token))
2379
self.repository.unlock()
2382
def lock_read(self):
2383
"""Lock the branch for read operations.
2385
:return: A bzrlib.lock.LogicalLockResult.
2387
if not self.is_locked():
2388
self._note_lock('r')
2389
# All-in-one needs to always unlock/lock.
2390
repo_control = getattr(self.repository, 'control_files', None)
2391
if self.control_files == repo_control or not self.is_locked():
2392
self.repository._warn_if_deprecated(self)
2393
self.repository.lock_read()
2398
self.control_files.lock_read()
2399
return LogicalLockResult(self.unlock)
2402
self.repository.unlock()
2405
@only_raises(errors.LockNotHeld, errors.LockBroken)
2408
self.control_files.unlock()
2410
# All-in-one needs to always unlock/lock.
2411
repo_control = getattr(self.repository, 'control_files', None)
2412
if (self.control_files == repo_control or
2413
not self.control_files.is_locked()):
2414
self.repository.unlock()
2415
if not self.control_files.is_locked():
2416
# we just released the lock
2417
self._clear_cached_state()
2419
def peek_lock_mode(self):
2420
if self.control_files._lock_count == 0:
2423
return self.control_files._lock_mode
2425
def get_physical_lock_status(self):
2426
return self.control_files.get_physical_lock_status()
2429
def print_file(self, file, revision_id):
2430
"""See Branch.print_file."""
2431
return self.repository.print_file(file, revision_id)
2433
def _write_revision_history(self, history):
2434
"""Factored out of set_revision_history.
2436
This performs the actual writing to disk.
2437
It is intended to be called by BzrBranch5.set_revision_history."""
2438
self._transport.put_bytes(
2439
'revision-history', '\n'.join(history),
2440
mode=self.bzrdir._get_file_mode())
2443
def set_revision_history(self, rev_history):
2444
"""See Branch.set_revision_history."""
2445
if 'evil' in debug.debug_flags:
2446
mutter_callsite(3, "set_revision_history scales with history.")
2447
check_not_reserved_id = _mod_revision.check_not_reserved_id
2448
for rev_id in rev_history:
2449
check_not_reserved_id(rev_id)
2450
if Branch.hooks['post_change_branch_tip']:
2451
# Don't calculate the last_revision_info() if there are no hooks
2453
old_revno, old_revid = self.last_revision_info()
2454
if len(rev_history) == 0:
2455
revid = _mod_revision.NULL_REVISION
2457
revid = rev_history[-1]
2458
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2459
self._write_revision_history(rev_history)
2460
self._clear_cached_state()
2461
self._cache_revision_history(rev_history)
2462
for hook in Branch.hooks['set_rh']:
2463
hook(self, rev_history)
2464
if Branch.hooks['post_change_branch_tip']:
2465
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2467
def _synchronize_history(self, destination, revision_id):
2468
"""Synchronize last revision and revision history between branches.
2470
This version is most efficient when the destination is also a
2471
BzrBranch5, but works for BzrBranch6 as long as the revision
2472
history is the true lefthand parent history, and all of the revisions
2473
are in the destination's repository. If not, set_revision_history
2476
:param destination: The branch to copy the history into
2477
:param revision_id: The revision-id to truncate history at. May
2478
be None to copy complete history.
2480
if not isinstance(destination._format, BzrBranchFormat5):
2481
super(BzrBranch, self)._synchronize_history(
2482
destination, revision_id)
2484
if revision_id == _mod_revision.NULL_REVISION:
2487
new_history = self.revision_history()
2488
if revision_id is not None and new_history != []:
2490
new_history = new_history[:new_history.index(revision_id) + 1]
2492
rev = self.repository.get_revision(revision_id)
2493
new_history = rev.get_history(self.repository)[1:]
2494
destination.set_revision_history(new_history)
2497
def set_last_revision_info(self, revno, revision_id):
2498
"""Set the last revision of this branch.
2500
The caller is responsible for checking that the revno is correct
2501
for this revision id.
2503
It may be possible to set the branch last revision to an id not
2504
present in the repository. However, branches can also be
2505
configured to check constraints on history, in which case this may not
2508
revision_id = _mod_revision.ensure_null(revision_id)
2509
# this old format stores the full history, but this api doesn't
2510
# provide it, so we must generate, and might as well check it's
2512
history = self._lefthand_history(revision_id)
2513
if len(history) != revno:
2514
raise AssertionError('%d != %d' % (len(history), revno))
2515
self.set_revision_history(history)
2517
def _gen_revision_history(self):
2518
history = self._transport.get_bytes('revision-history').split('\n')
2519
if history[-1:] == ['']:
2520
# There shouldn't be a trailing newline, but just in case.
2525
def generate_revision_history(self, revision_id, last_rev=None,
2527
"""Create a new revision history that will finish with revision_id.
2529
:param revision_id: the new tip to use.
2530
:param last_rev: The previous last_revision. If not None, then this
2531
must be a ancestory of revision_id, or DivergedBranches is raised.
2532
:param other_branch: The other branch that DivergedBranches should
2533
raise with respect to.
2535
self.set_revision_history(self._lefthand_history(revision_id,
2536
last_rev, other_branch))
2538
def basis_tree(self):
2539
"""See Branch.basis_tree."""
2540
return self.repository.revision_tree(self.last_revision())
2542
def _get_parent_location(self):
2543
_locs = ['parent', 'pull', 'x-pull']
2546
return self._transport.get_bytes(l).strip('\n')
2547
except errors.NoSuchFile:
2551
def _basic_push(self, target, overwrite, stop_revision):
2552
"""Basic implementation of push without bound branches or hooks.
2554
Must be called with source read locked and target write locked.
2556
result = BranchPushResult()
2557
result.source_branch = self
2558
result.target_branch = target
2559
result.old_revno, result.old_revid = target.last_revision_info()
2560
self.update_references(target)
2561
if result.old_revid != self.last_revision():
2562
# We assume that during 'push' this repository is closer than
2564
graph = self.repository.get_graph(target.repository)
2565
target.update_revisions(self, stop_revision,
2566
overwrite=overwrite, graph=graph)
2567
if self._push_should_merge_tags():
2568
result.tag_conflicts = self.tags.merge_to(target.tags,
2570
result.new_revno, result.new_revid = target.last_revision_info()
2573
def get_stacked_on_url(self):
2574
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2576
def set_push_location(self, location):
2577
"""See Branch.set_push_location."""
2578
self.get_config().set_user_option(
2579
'push_location', location,
2580
store=_mod_config.STORE_LOCATION_NORECURSE)
2582
def _set_parent_location(self, url):
2584
self._transport.delete('parent')
2586
self._transport.put_bytes('parent', url + '\n',
2587
mode=self.bzrdir._get_file_mode())
2590
class BzrBranch5(BzrBranch):
2591
"""A format 5 branch. This supports new features over plain branches.
2593
It has support for a master_branch which is the data for bound branches.
2596
def get_bound_location(self):
2598
return self._transport.get_bytes('bound')[:-1]
2599
except errors.NoSuchFile:
2603
def get_master_branch(self, possible_transports=None):
2604
"""Return the branch we are bound to.
2606
:return: Either a Branch, or None
2608
This could memoise the branch, but if thats done
2609
it must be revalidated on each new lock.
2610
So for now we just don't memoise it.
2611
# RBC 20060304 review this decision.
2613
bound_loc = self.get_bound_location()
2617
return Branch.open(bound_loc,
2618
possible_transports=possible_transports)
2619
except (errors.NotBranchError, errors.ConnectionError), e:
2620
raise errors.BoundBranchConnectionFailure(
2624
def set_bound_location(self, location):
2625
"""Set the target where this branch is bound to.
2627
:param location: URL to the target branch
2630
self._transport.put_bytes('bound', location+'\n',
2631
mode=self.bzrdir._get_file_mode())
2634
self._transport.delete('bound')
2635
except errors.NoSuchFile:
2640
def bind(self, other):
2641
"""Bind this branch to the branch other.
2643
This does not push or pull data between the branches, though it does
2644
check for divergence to raise an error when the branches are not
2645
either the same, or one a prefix of the other. That behaviour may not
2646
be useful, so that check may be removed in future.
2648
:param other: The branch to bind to
2651
# TODO: jam 20051230 Consider checking if the target is bound
2652
# It is debatable whether you should be able to bind to
2653
# a branch which is itself bound.
2654
# Committing is obviously forbidden,
2655
# but binding itself may not be.
2656
# Since we *have* to check at commit time, we don't
2657
# *need* to check here
2659
# we want to raise diverged if:
2660
# last_rev is not in the other_last_rev history, AND
2661
# other_last_rev is not in our history, and do it without pulling
2663
self.set_bound_location(other.base)
2667
"""If bound, unbind"""
2668
return self.set_bound_location(None)
2671
def update(self, possible_transports=None):
2672
"""Synchronise this branch with the master branch if any.
2674
:return: None or the last_revision that was pivoted out during the
2677
master = self.get_master_branch(possible_transports)
2678
if master is not None:
2679
old_tip = _mod_revision.ensure_null(self.last_revision())
2680
self.pull(master, overwrite=True)
2681
if self.repository.get_graph().is_ancestor(old_tip,
2682
_mod_revision.ensure_null(self.last_revision())):
2688
class BzrBranch8(BzrBranch5):
2689
"""A branch that stores tree-reference locations."""
2691
def _open_hook(self):
2692
if self._ignore_fallbacks:
2695
url = self.get_stacked_on_url()
2696
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2697
errors.UnstackableBranchFormat):
2700
for hook in Branch.hooks['transform_fallback_location']:
2701
url = hook(self, url)
2703
hook_name = Branch.hooks.get_hook_name(hook)
2704
raise AssertionError(
2705
"'transform_fallback_location' hook %s returned "
2706
"None, not a URL." % hook_name)
2707
self._activate_fallback_location(url)
2709
def __init__(self, *args, **kwargs):
2710
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2711
super(BzrBranch8, self).__init__(*args, **kwargs)
2712
self._last_revision_info_cache = None
2713
self._reference_info = None
2715
def _clear_cached_state(self):
2716
super(BzrBranch8, self)._clear_cached_state()
2717
self._last_revision_info_cache = None
2718
self._reference_info = None
2720
def _last_revision_info(self):
2721
revision_string = self._transport.get_bytes('last-revision')
2722
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2723
revision_id = cache_utf8.get_cached_utf8(revision_id)
2725
return revno, revision_id
2727
def _write_last_revision_info(self, revno, revision_id):
2728
"""Simply write out the revision id, with no checks.
2730
Use set_last_revision_info to perform this safely.
2732
Does not update the revision_history cache.
2733
Intended to be called by set_last_revision_info and
2734
_write_revision_history.
2736
revision_id = _mod_revision.ensure_null(revision_id)
2737
out_string = '%d %s\n' % (revno, revision_id)
2738
self._transport.put_bytes('last-revision', out_string,
2739
mode=self.bzrdir._get_file_mode())
2742
def set_last_revision_info(self, revno, revision_id):
2743
revision_id = _mod_revision.ensure_null(revision_id)
2744
old_revno, old_revid = self.last_revision_info()
2745
if self._get_append_revisions_only():
2746
self._check_history_violation(revision_id)
2747
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2748
self._write_last_revision_info(revno, revision_id)
2749
self._clear_cached_state()
2750
self._last_revision_info_cache = revno, revision_id
2751
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2753
def _synchronize_history(self, destination, revision_id):
2754
"""Synchronize last revision and revision history between branches.
2756
:see: Branch._synchronize_history
2758
# XXX: The base Branch has a fast implementation of this method based
2759
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2760
# that uses set_revision_history. This class inherits from BzrBranch5,
2761
# but wants the fast implementation, so it calls
2762
# Branch._synchronize_history directly.
2763
Branch._synchronize_history(self, destination, revision_id)
2765
def _check_history_violation(self, revision_id):
2766
last_revision = _mod_revision.ensure_null(self.last_revision())
2767
if _mod_revision.is_null(last_revision):
2769
if last_revision not in self._lefthand_history(revision_id):
2770
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2772
def _gen_revision_history(self):
2773
"""Generate the revision history from last revision
2775
last_revno, last_revision = self.last_revision_info()
2776
self._extend_partial_history(stop_index=last_revno-1)
2777
return list(reversed(self._partial_revision_history_cache))
2779
def _write_revision_history(self, history):
2780
"""Factored out of set_revision_history.
2782
This performs the actual writing to disk, with format-specific checks.
2783
It is intended to be called by BzrBranch5.set_revision_history.
2785
if len(history) == 0:
2786
last_revision = 'null:'
2788
if history != self._lefthand_history(history[-1]):
2789
raise errors.NotLefthandHistory(history)
2790
last_revision = history[-1]
2791
if self._get_append_revisions_only():
2792
self._check_history_violation(last_revision)
2793
self._write_last_revision_info(len(history), last_revision)
2796
def _set_parent_location(self, url):
2797
"""Set the parent branch"""
2798
self._set_config_location('parent_location', url, make_relative=True)
2801
def _get_parent_location(self):
2802
"""Set the parent branch"""
2803
return self._get_config_location('parent_location')
2806
def _set_all_reference_info(self, info_dict):
2807
"""Replace all reference info stored in a branch.
2809
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2812
writer = rio.RioWriter(s)
2813
for key, (tree_path, branch_location) in info_dict.iteritems():
2814
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2815
branch_location=branch_location)
2816
writer.write_stanza(stanza)
2817
self._transport.put_bytes('references', s.getvalue())
2818
self._reference_info = info_dict
2821
def _get_all_reference_info(self):
2822
"""Return all the reference info stored in a branch.
2824
:return: A dict of {file_id: (tree_path, branch_location)}
2826
if self._reference_info is not None:
2827
return self._reference_info
2828
rio_file = self._transport.get('references')
2830
stanzas = rio.read_stanzas(rio_file)
2831
info_dict = dict((s['file_id'], (s['tree_path'],
2832
s['branch_location'])) for s in stanzas)
2835
self._reference_info = info_dict
2838
def set_reference_info(self, file_id, tree_path, branch_location):
2839
"""Set the branch location to use for a tree reference.
2841
:param file_id: The file-id of the tree reference.
2842
:param tree_path: The path of the tree reference in the tree.
2843
:param branch_location: The location of the branch to retrieve tree
2846
info_dict = self._get_all_reference_info()
2847
info_dict[file_id] = (tree_path, branch_location)
2848
if None in (tree_path, branch_location):
2849
if tree_path is not None:
2850
raise ValueError('tree_path must be None when branch_location'
2852
if branch_location is not None:
2853
raise ValueError('branch_location must be None when tree_path'
2855
del info_dict[file_id]
2856
self._set_all_reference_info(info_dict)
2858
def get_reference_info(self, file_id):
2859
"""Get the tree_path and branch_location for a tree reference.
2861
:return: a tuple of (tree_path, branch_location)
2863
return self._get_all_reference_info().get(file_id, (None, None))
2865
def reference_parent(self, file_id, path, possible_transports=None):
2866
"""Return the parent branch for a tree-reference file_id.
2868
:param file_id: The file_id of the tree reference
2869
:param path: The path of the file_id in the tree
2870
:return: A branch associated with the file_id
2872
branch_location = self.get_reference_info(file_id)[1]
2873
if branch_location is None:
2874
return Branch.reference_parent(self, file_id, path,
2875
possible_transports)
2876
branch_location = urlutils.join(self.user_url, branch_location)
2877
return Branch.open(branch_location,
2878
possible_transports=possible_transports)
2880
def set_push_location(self, location):
2881
"""See Branch.set_push_location."""
2882
self._set_config_location('push_location', location)
2884
def set_bound_location(self, location):
2885
"""See Branch.set_push_location."""
2887
config = self.get_config()
2888
if location is None:
2889
if config.get_user_option('bound') != 'True':
2892
config.set_user_option('bound', 'False', warn_masked=True)
2895
self._set_config_location('bound_location', location,
2897
config.set_user_option('bound', 'True', warn_masked=True)
2900
def _get_bound_location(self, bound):
2901
"""Return the bound location in the config file.
2903
Return None if the bound parameter does not match"""
2904
config = self.get_config()
2905
config_bound = (config.get_user_option('bound') == 'True')
2906
if config_bound != bound:
2908
return self._get_config_location('bound_location', config=config)
2910
def get_bound_location(self):
2911
"""See Branch.set_push_location."""
2912
return self._get_bound_location(True)
2914
def get_old_bound_location(self):
2915
"""See Branch.get_old_bound_location"""
2916
return self._get_bound_location(False)
2918
def get_stacked_on_url(self):
2919
# you can always ask for the URL; but you might not be able to use it
2920
# if the repo can't support stacking.
2921
## self._check_stackable_repo()
2922
stacked_url = self._get_config_location('stacked_on_location')
2923
if stacked_url is None:
2924
raise errors.NotStacked(self)
2927
def _get_append_revisions_only(self):
2928
return self.get_config(
2929
).get_user_option_as_bool('append_revisions_only')
2932
def generate_revision_history(self, revision_id, last_rev=None,
2934
"""See BzrBranch5.generate_revision_history"""
2935
history = self._lefthand_history(revision_id, last_rev, other_branch)
2936
revno = len(history)
2937
self.set_last_revision_info(revno, revision_id)
2940
def get_rev_id(self, revno, history=None):
2941
"""Find the revision id of the specified revno."""
2943
return _mod_revision.NULL_REVISION
2945
last_revno, last_revision_id = self.last_revision_info()
2946
if revno <= 0 or revno > last_revno:
2947
raise errors.NoSuchRevision(self, revno)
2949
if history is not None:
2950
return history[revno - 1]
2952
index = last_revno - revno
2953
if len(self._partial_revision_history_cache) <= index:
2954
self._extend_partial_history(stop_index=index)
2955
if len(self._partial_revision_history_cache) > index:
2956
return self._partial_revision_history_cache[index]
2958
raise errors.NoSuchRevision(self, revno)
2961
def revision_id_to_revno(self, revision_id):
2962
"""Given a revision id, return its revno"""
2963
if _mod_revision.is_null(revision_id):
2966
index = self._partial_revision_history_cache.index(revision_id)
2968
self._extend_partial_history(stop_revision=revision_id)
2969
index = len(self._partial_revision_history_cache) - 1
2970
if self._partial_revision_history_cache[index] != revision_id:
2971
raise errors.NoSuchRevision(self, revision_id)
2972
return self.revno() - index
2975
class BzrBranch7(BzrBranch8):
2976
"""A branch with support for a fallback repository."""
2978
def set_reference_info(self, file_id, tree_path, branch_location):
2979
Branch.set_reference_info(self, file_id, tree_path, branch_location)
2981
def get_reference_info(self, file_id):
2982
Branch.get_reference_info(self, file_id)
2984
def reference_parent(self, file_id, path, possible_transports=None):
2985
return Branch.reference_parent(self, file_id, path,
2986
possible_transports)
2989
class BzrBranch6(BzrBranch7):
2990
"""See BzrBranchFormat6 for the capabilities of this branch.
2992
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2996
def get_stacked_on_url(self):
2997
raise errors.UnstackableBranchFormat(self._format, self.user_url)
3000
1968
######################################################################
3001
1969
# results of operations
3225
2114
raise NotImplementedError(self.push)
2117
def copy_content_into(self, revision_id=None):
2118
"""Copy the content of source into target
2120
revision_id: if not None, the revision history in the new branch will
2121
be truncated to end with revision_id.
2123
raise NotImplementedError(self.copy_content_into)
2126
def fetch(self, stop_revision=None, limit=None):
2129
:param stop_revision: Last revision to fetch
2130
:param limit: Optional rough limit of revisions to fetch
2132
raise NotImplementedError(self.fetch)
2135
def _fix_overwrite_type(overwrite):
2136
if isinstance(overwrite, bool):
2138
return ["history", "tags"]
3228
2144
class GenericInterBranch(InterBranch):
3229
"""InterBranch implementation that uses public Branch functions.
3233
def _get_branch_formats_to_test():
3234
return BranchFormat._default_format, BranchFormat._default_format
3236
def update_revisions(self, stop_revision=None, overwrite=False,
3238
"""See InterBranch.update_revisions()."""
2145
"""InterBranch implementation that uses public Branch functions."""
2148
def is_compatible(klass, source, target):
2149
# GenericBranch uses the public API, so always compatible
2153
def _get_branch_formats_to_test(klass):
2154
return [(format_registry.get_default(), format_registry.get_default())]
2157
def unwrap_format(klass, format):
2158
if isinstance(format, remote.RemoteBranchFormat):
2159
format._ensure_real()
2160
return format._custom_format
2164
def copy_content_into(self, revision_id=None):
2165
"""Copy the content of source into target
2167
revision_id: if not None, the revision history in the new branch will
2168
be truncated to end with revision_id.
2170
self.source.update_references(self.target)
2171
self.source._synchronize_history(self.target, revision_id)
2173
parent = self.source.get_parent()
2174
except errors.InaccessibleParent as e:
2175
mutter('parent was not accessible to copy: %s', e)
2178
self.target.set_parent(parent)
2179
if self.source._push_should_merge_tags():
2180
self.source.tags.merge_to(self.target.tags)
2183
def fetch(self, stop_revision=None, limit=None):
2184
if self.target.base == self.source.base:
3239
2186
self.source.lock_read()
3241
other_revno, other_last_revision = self.source.last_revision_info()
3242
stop_revno = None # unknown
3243
if stop_revision is None:
3244
stop_revision = other_last_revision
3245
if _mod_revision.is_null(stop_revision):
3246
# if there are no commits, we're done.
3248
stop_revno = other_revno
3250
# what's the current last revision, before we fetch [and change it
3252
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3253
# we fetch here so that we don't process data twice in the common
3254
# case of having something to pull, and so that the check for
3255
# already merged can operate on the just fetched graph, which will
3256
# be cached in memory.
3257
self.target.fetch(self.source, stop_revision)
3258
# Check to see if one is an ancestor of the other
3261
graph = self.target.repository.get_graph()
3262
if self.target._check_if_descendant_or_diverged(
3263
stop_revision, last_rev, graph, self.source):
3264
# stop_revision is a descendant of last_rev, but we aren't
3265
# overwriting, so we're done.
3267
if stop_revno is None:
3269
graph = self.target.repository.get_graph()
3270
this_revno, this_last_revision = \
3271
self.target.last_revision_info()
3272
stop_revno = graph.find_distance_to_null(stop_revision,
3273
[(other_last_revision, other_revno),
3274
(this_last_revision, this_revno)])
3275
self.target.set_last_revision_info(stop_revno, stop_revision)
2188
fetch_spec_factory = fetch.FetchSpecFactory()
2189
fetch_spec_factory.source_branch = self.source
2190
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2191
fetch_spec_factory.source_repo = self.source.repository
2192
fetch_spec_factory.target_repo = self.target.repository
2193
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
2194
fetch_spec_factory.limit = limit
2195
fetch_spec = fetch_spec_factory.make_fetch_spec()
2196
return self.target.repository.fetch(self.source.repository,
2197
fetch_spec=fetch_spec)
3277
2199
self.source.unlock()
2202
def _update_revisions(self, stop_revision=None, overwrite=False,
2204
other_revno, other_last_revision = self.source.last_revision_info()
2205
stop_revno = None # unknown
2206
if stop_revision is None:
2207
stop_revision = other_last_revision
2208
if _mod_revision.is_null(stop_revision):
2209
# if there are no commits, we're done.
2211
stop_revno = other_revno
2213
# what's the current last revision, before we fetch [and change it
2215
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2216
# we fetch here so that we don't process data twice in the common
2217
# case of having something to pull, and so that the check for
2218
# already merged can operate on the just fetched graph, which will
2219
# be cached in memory.
2220
self.fetch(stop_revision=stop_revision)
2221
# Check to see if one is an ancestor of the other
2224
graph = self.target.repository.get_graph()
2225
if self.target._check_if_descendant_or_diverged(
2226
stop_revision, last_rev, graph, self.source):
2227
# stop_revision is a descendant of last_rev, but we aren't
2228
# overwriting, so we're done.
2230
if stop_revno is None:
2232
graph = self.target.repository.get_graph()
2233
this_revno, this_last_revision = \
2234
self.target.last_revision_info()
2235
stop_revno = graph.find_distance_to_null(stop_revision,
2236
[(other_last_revision, other_revno),
2237
(this_last_revision, this_revno)])
2238
self.target.set_last_revision_info(stop_revno, stop_revision)
3279
2241
def pull(self, overwrite=False, stop_revision=None,
2242
possible_transports=None, run_hooks=True,
2243
_override_hook_target=None, local=False):
2244
"""Pull from source into self, updating my master if any.
2246
:param run_hooks: Private parameter - if false, this branch
2247
is being called because it's the master of the primary branch,
2248
so it should not run its hooks.
2250
bound_location = self.target.get_bound_location()
2251
if local and not bound_location:
2252
raise errors.LocalRequiresBoundBranch()
2253
master_branch = None
2254
source_is_master = False
2256
# bound_location comes from a config file, some care has to be
2257
# taken to relate it to source.user_url
2258
normalized = urlutils.normalize_url(bound_location)
2260
relpath = self.source.user_transport.relpath(normalized)
2261
source_is_master = (relpath == '')
2262
except (errors.PathNotChild, urlutils.InvalidURL):
2263
source_is_master = False
2264
if not local and bound_location and not source_is_master:
2265
# not pulling from master, so we need to update master.
2266
master_branch = self.target.get_master_branch(possible_transports)
2267
master_branch.lock_write()
2270
# pull from source into master.
2271
master_branch.pull(self.source, overwrite, stop_revision,
2273
return self._pull(overwrite,
2274
stop_revision, _hook_master=master_branch,
2275
run_hooks=run_hooks,
2276
_override_hook_target=_override_hook_target,
2277
merge_tags_to_master=not source_is_master)
2280
master_branch.unlock()
2282
def push(self, overwrite=False, stop_revision=None, lossy=False,
2283
_override_hook_source_branch=None):
2284
"""See InterBranch.push.
2286
This is the basic concrete implementation of push()
2288
:param _override_hook_source_branch: If specified, run the hooks
2289
passing this Branch as the source, rather than self. This is for
2290
use of RemoteBranch, where push is delegated to the underlying
2294
raise errors.LossyPushToSameVCS(self.source, self.target)
2295
# TODO: Public option to disable running hooks - should be trivial but
2298
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
2299
op.add_cleanup(self.source.lock_read().unlock)
2300
op.add_cleanup(self.target.lock_write().unlock)
2301
return op.run(overwrite, stop_revision,
2302
_override_hook_source_branch=_override_hook_source_branch)
2304
def _basic_push(self, overwrite, stop_revision):
2305
"""Basic implementation of push without bound branches or hooks.
2307
Must be called with source read locked and target write locked.
2309
result = BranchPushResult()
2310
result.source_branch = self.source
2311
result.target_branch = self.target
2312
result.old_revno, result.old_revid = self.target.last_revision_info()
2313
self.source.update_references(self.target)
2314
overwrite = _fix_overwrite_type(overwrite)
2315
if result.old_revid != stop_revision:
2316
# We assume that during 'push' this repository is closer than
2318
graph = self.source.repository.get_graph(self.target.repository)
2319
self._update_revisions(stop_revision,
2320
overwrite=("history" in overwrite),
2322
if self.source._push_should_merge_tags():
2323
result.tag_updates, result.tag_conflicts = (
2324
self.source.tags.merge_to(
2325
self.target.tags, "tags" in overwrite))
2326
result.new_revno, result.new_revid = self.target.last_revision_info()
2329
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
2330
_override_hook_source_branch=None):
2331
"""Push from source into target, and into target's master if any.
2334
if _override_hook_source_branch:
2335
result.source_branch = _override_hook_source_branch
2336
for hook in Branch.hooks['post_push']:
2339
bound_location = self.target.get_bound_location()
2340
if bound_location and self.target.base != bound_location:
2341
# there is a master branch.
2343
# XXX: Why the second check? Is it even supported for a branch to
2344
# be bound to itself? -- mbp 20070507
2345
master_branch = self.target.get_master_branch()
2346
master_branch.lock_write()
2347
operation.add_cleanup(master_branch.unlock)
2348
# push into the master from the source branch.
2349
master_inter = InterBranch.get(self.source, master_branch)
2350
master_inter._basic_push(overwrite, stop_revision)
2351
# and push into the target branch from the source. Note that
2352
# we push from the source branch again, because it's considered
2353
# the highest bandwidth repository.
2354
result = self._basic_push(overwrite, stop_revision)
2355
result.master_branch = master_branch
2356
result.local_branch = self.target
2358
master_branch = None
2360
result = self._basic_push(overwrite, stop_revision)
2361
# TODO: Why set master_branch and local_branch if there's no
2362
# binding? Maybe cleaner to just leave them unset? -- mbp
2364
result.master_branch = self.target
2365
result.local_branch = None
2369
def _pull(self, overwrite=False, stop_revision=None,
3280
2370
possible_transports=None, _hook_master=None, run_hooks=True,
3281
_override_hook_target=None, local=False):
2371
_override_hook_target=None, local=False,
2372
merge_tags_to_master=True):
3282
2373
"""See Branch.pull.
2375
This function is the core worker, used by GenericInterBranch.pull to
2376
avoid duplication when pulling source->master and source->local.
3284
2378
:param _hook_master: Private parameter - set the branch to
3285
2379
be supplied as the master to pull hooks.
3286
2380
:param run_hooks: Private parameter - if false, this branch
3287
2381
is being called because it's the master of the primary branch,
3288
2382
so it should not run its hooks.
2383
is being called because it's the master of the primary branch,
2384
so it should not run its hooks.
3289
2385
:param _override_hook_target: Private parameter - set the branch to be
3290
2386
supplied as the target_branch to pull hooks.
3291
2387
:param local: Only update the local branch, and not the bound branch.