15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
20
18
from bzrlib.lazy_import import lazy_import
21
19
lazy_import(globals(), """
22
from copy import deepcopy
23
from unittest import TestSuite
24
from warnings import warn
27
20
from bzrlib import (
30
23
config as _mod_config,
35
28
revision as _mod_revision,
42
from bzrlib.config import BranchConfig, TreeConfig
43
from bzrlib.lockable_files import LockableFiles, TransportLock
34
from bzrlib.config import BranchConfig
44
35
from bzrlib.tag import (
50
41
from bzrlib.decorators import needs_read_lock, needs_write_lock
51
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
52
HistoryMissing, InvalidRevisionId,
53
InvalidRevisionNumber, LockError, NoSuchFile,
54
NoSuchRevision, NoWorkingTree, NotVersionedError,
55
NotBranchError, UninitializableFormat,
56
UnlistableStore, UnlistableBranch,
58
42
from bzrlib.hooks import Hooks
59
from bzrlib.symbol_versioning import (deprecated_function,
63
zero_eight, zero_nine, zero_sixteen,
43
from bzrlib.symbol_versioning import (deprecated_method,
65
from bzrlib.trace import mutter, note
46
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
68
49
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
118
99
master.break_lock()
121
@deprecated_method(zero_eight)
122
def open_downlevel(base):
123
"""Open a branch which may be of an old format."""
124
return Branch.open(base, _unsupported=True)
127
def open(base, _unsupported=False):
102
def open(base, _unsupported=False, possible_transports=None):
128
103
"""Open the branch rooted at base.
130
105
For instance, if the branch is at URL/.bzr/branch,
131
106
Branch.open(URL) -> a Branch instance.
133
control = bzrdir.BzrDir.open(base, _unsupported)
134
return control.open_branch(_unsupported)
137
def open_containing(url):
108
control = bzrdir.BzrDir.open(base, _unsupported,
109
possible_transports=possible_transports)
110
return control.open_branch(_unsupported)
113
def open_from_transport(transport, _unsupported=False):
114
"""Open the branch rooted at transport"""
115
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
116
return control.open_branch(_unsupported)
119
def open_containing(url, possible_transports=None):
138
120
"""Open an existing branch which contains url.
140
122
This probes for a branch at url, and searches upwards from there.
145
127
format, UnknownFormatError or UnsupportedFormatError are raised.
146
128
If there is one, it is returned, along with the unused portion of url.
148
control, relpath = bzrdir.BzrDir.open_containing(url)
130
control, relpath = bzrdir.BzrDir.open_containing(url,
149
132
return control.open_branch(), relpath
152
@deprecated_function(zero_eight)
153
def initialize(base):
154
"""Create a new working tree and branch, rooted at 'base' (url)
156
NOTE: This will soon be deprecated in favour of creation
159
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
161
@deprecated_function(zero_eight)
162
def setup_caching(self, cache_root):
163
"""Subclasses that care about caching should override this, and set
164
up cached stores located under cache_root.
166
NOTE: This is unused.
170
134
def get_config(self):
171
135
return BranchConfig(self)
519
476
def get_rev_id(self, revno, history=None):
520
477
"""Find the revision id of the specified revno."""
479
return _mod_revision.NULL_REVISION
523
480
if history is None:
524
481
history = self.revision_history()
525
482
if revno <= 0 or revno > len(history):
526
483
raise errors.NoSuchRevision(self, revno)
527
484
return history[revno - 1]
529
def pull(self, source, overwrite=False, stop_revision=None):
486
def pull(self, source, overwrite=False, stop_revision=None,
487
possible_transports=None):
530
488
"""Mirror source into this branch.
532
490
This branch is considered to be 'local', having low latency.
775
735
def create_checkout(self, to_location, revision_id=None,
736
lightweight=False, accelerator_tree=None,
777
738
"""Create a checkout of a branch.
779
740
:param to_location: The url to produce the checkout at
780
741
:param revision_id: The revision to check out
781
742
:param lightweight: If True, produce a lightweight checkout, otherwise,
782
743
produce a bound branch (heavyweight checkout)
744
:param accelerator_tree: A tree which can be used for retrieving file
745
contents more quickly than the revision tree, i.e. a workingtree.
746
The revision tree will be used for cases where accelerator_tree's
747
content is different.
748
:param hardlink: If true, hard-link files from accelerator_tree,
783
750
:return: The tree of the created checkout
785
752
t = transport.get_transport(to_location)
788
except errors.FileExists:
791
755
format = self._get_checkout_format()
792
756
checkout = format.initialize_on_transport(t)
793
BranchReferenceFormat().initialize(checkout, self)
757
from_branch = BranchReferenceFormat().initialize(checkout, self)
795
759
format = self._get_checkout_format()
796
760
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1015
1002
# is read locked and the target branches write locked. The local
1016
1003
# branch is the low-latency branch.
1017
1004
self['post_pull'] = []
1005
# invoked before a commit operation takes place.
1006
# the api signature is
1007
# (local, master, old_revno, old_revid, future_revno, future_revid,
1008
# tree_delta, future_tree).
1009
# old_revid is NULL_REVISION for the first commit to a branch
1010
# tree_delta is a TreeDelta object describing changes from the basis
1011
# revision, hooks MUST NOT modify this delta
1012
# future_tree is an in-memory tree obtained from
1013
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1014
self['pre_commit'] = []
1018
1015
# invoked after a commit operation completes.
1019
1016
# the api signature is
1020
1017
# (local, master, old_revno, old_revid, new_revno, new_revid)
1123
1120
_control_files=control_files,
1124
1121
a_bzrdir=a_bzrdir,
1125
1122
_repository=a_bzrdir.find_repository())
1127
raise NotBranchError(path=transport.base)
1123
except errors.NoSuchFile:
1124
raise errors.NotBranchError(path=transport.base)
1130
1127
class BzrBranchFormat6(BzrBranchFormat5):
1131
"""Branch format with last-revision
1128
"""Branch format with last-revision and tags.
1133
1130
Unlike previous formats, this has no explicit revision history. Instead,
1134
1131
this just stores the last-revision, and the left-hand history leading
1135
1132
up to there is the history.
1137
1134
This format was introduced in bzr 0.15
1135
and became the default in 0.91.
1140
1138
def get_format_string(self):
1193
1190
def get_format_description(self):
1194
1191
"""See BranchFormat.get_format_description()."""
1195
1192
return "Checkout reference format 1"
1197
1194
def get_reference(self, a_bzrdir):
1198
1195
"""See BranchFormat.get_reference()."""
1199
1196
transport = a_bzrdir.get_branch_transport(None)
1200
1197
return transport.get('location').read()
1199
def set_reference(self, a_bzrdir, to_branch):
1200
"""See BranchFormat.set_reference()."""
1201
transport = a_bzrdir.get_branch_transport(None)
1202
location = transport.put_bytes('location', to_branch.base)
1202
1204
def initialize(self, a_bzrdir, target_branch=None):
1203
1205
"""Create a branch of this format in a_bzrdir."""
1204
1206
if target_branch is None:
1254
1260
# formats which have no format string are not discoverable
1255
1261
# and not independently creatable, so are not registered.
1256
__default_format = BzrBranchFormat5()
1257
BranchFormat.register_format(__default_format)
1262
__format5 = BzrBranchFormat5()
1263
__format6 = BzrBranchFormat6()
1264
BranchFormat.register_format(__format5)
1258
1265
BranchFormat.register_format(BranchReferenceFormat())
1259
BranchFormat.register_format(BzrBranchFormat6())
1260
BranchFormat.set_default_format(__default_format)
1266
BranchFormat.register_format(__format6)
1267
BranchFormat.set_default_format(__format6)
1261
1268
_legacy_formats = [BzrBranchFormat4(),
1355
1362
"""See Branch.print_file."""
1356
1363
return self.repository.print_file(file, revision_id)
1359
def append_revision(self, *revision_ids):
1360
"""See Branch.append_revision."""
1361
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1362
for revision_id in revision_ids:
1363
_mod_revision.check_not_reserved_id(revision_id)
1364
mutter("add {%s} to revision-history" % revision_id)
1365
rev_history = self.revision_history()
1366
rev_history.extend(revision_ids)
1367
self.set_revision_history(rev_history)
1369
1365
def _write_revision_history(self, history):
1370
1366
"""Factored out of set_revision_history.
1387
1384
@needs_write_lock
1388
1385
def set_last_revision_info(self, revno, revision_id):
1389
revision_id = osutils.safe_revision_id(revision_id)
1386
"""Set the last revision of this branch.
1388
The caller is responsible for checking that the revno is correct
1389
for this revision id.
1391
It may be possible to set the branch last revision to an id not
1392
present in the repository. However, branches can also be
1393
configured to check constraints on history, in which case this may not
1396
revision_id = _mod_revision.ensure_null(revision_id)
1390
1397
history = self._lefthand_history(revision_id)
1391
1398
assert len(history) == revno, '%d != %d' % (len(history), revno)
1392
1399
self.set_revision_history(history)
1401
1408
def _lefthand_history(self, revision_id, last_rev=None,
1402
1409
other_branch=None):
1410
if 'evil' in debug.debug_flags:
1411
mutter_callsite(4, "_lefthand_history scales with history.")
1403
1412
# stop_revision must be a descendant of last_revision
1404
stop_graph = self.repository.get_revision_graph(revision_id)
1405
if last_rev is not None and last_rev not in stop_graph:
1406
# our previous tip is not merged into stop_revision
1407
raise errors.DivergedBranches(self, other_branch)
1413
graph = self.repository.get_graph()
1414
if last_rev is not None:
1415
if not graph.is_ancestor(last_rev, revision_id):
1416
# our previous tip is not merged into stop_revision
1417
raise errors.DivergedBranches(self, other_branch)
1408
1418
# make a new revision history from the graph
1419
parents_map = graph.get_parent_map([revision_id])
1420
if revision_id not in parents_map:
1421
raise errors.NoSuchRevision(self, revision_id)
1409
1422
current_rev_id = revision_id
1410
1423
new_history = []
1411
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1424
# Do not include ghosts or graph origin in revision_history
1425
while (current_rev_id in parents_map and
1426
len(parents_map[current_rev_id]) > 0):
1412
1427
new_history.append(current_rev_id)
1413
current_rev_id_parents = stop_graph[current_rev_id]
1415
current_rev_id = current_rev_id_parents[0]
1417
current_rev_id = None
1428
current_rev_id = parents_map[current_rev_id][0]
1429
parents_map = graph.get_parent_map([current_rev_id])
1418
1430
new_history.reverse()
1419
1431
return new_history
1429
1441
:param other_branch: The other branch that DivergedBranches should
1430
1442
raise with respect to.
1432
revision_id = osutils.safe_revision_id(revision_id)
1433
1444
self.set_revision_history(self._lefthand_history(revision_id,
1434
1445
last_rev, other_branch))
1436
1447
@needs_write_lock
1437
def update_revisions(self, other, stop_revision=None):
1448
def update_revisions(self, other, stop_revision=None, overwrite=False):
1438
1449
"""See Branch.update_revisions."""
1439
1450
other.lock_read()
1452
other_last_revno, other_last_revision = other.last_revision_info()
1441
1453
if stop_revision is None:
1442
stop_revision = other.last_revision()
1443
if stop_revision is None:
1454
stop_revision = other_last_revision
1455
if _mod_revision.is_null(stop_revision):
1444
1456
# if there are no commits, we're done.
1447
stop_revision = osutils.safe_revision_id(stop_revision)
1448
1458
# whats the current last revision, before we fetch [and change it
1450
last_rev = self.last_revision()
1451
# we fetch here regardless of whether we need to so that we pickup
1460
last_rev = _mod_revision.ensure_null(self.last_revision())
1461
# we fetch here so that we don't process data twice in the common
1462
# case of having something to pull, and so that the check for
1463
# already merged can operate on the just fetched graph, which will
1464
# be cached in memory.
1453
1465
self.fetch(other, stop_revision)
1454
my_ancestry = self.repository.get_ancestry(last_rev)
1455
if stop_revision in my_ancestry:
1456
# last_revision is a descendant of stop_revision
1458
self.generate_revision_history(stop_revision, last_rev=last_rev,
1466
# Check to see if one is an ancestor of the other
1468
heads = self.repository.get_graph().heads([stop_revision,
1470
if heads == set([last_rev]):
1471
# The current revision is a decendent of the target,
1474
elif heads == set([stop_revision, last_rev]):
1475
# These branches have diverged
1476
raise errors.DivergedBranches(self, other)
1477
assert heads == set([stop_revision])
1478
if other_last_revision == stop_revision:
1479
self.set_last_revision_info(other_last_revno,
1480
other_last_revision)
1482
# TODO: jam 2007-11-29 Is there a way to determine the
1483
# revno without searching all of history??
1485
self.generate_revision_history(stop_revision)
1487
self.generate_revision_history(stop_revision,
1488
last_rev=last_rev, other_branch=other)
1464
1493
"""See Branch.basis_tree."""
1465
1494
return self.repository.revision_tree(self.last_revision())
1467
@deprecated_method(zero_eight)
1468
def working_tree(self):
1469
"""Create a Working tree object for this branch."""
1471
from bzrlib.transport.local import LocalTransport
1472
if (self.base.find('://') != -1 or
1473
not isinstance(self._transport, LocalTransport)):
1474
raise NoWorkingTree(self.base)
1475
return self.bzrdir.open_workingtree()
1477
1496
@needs_write_lock
1478
1497
def pull(self, source, overwrite=False, stop_revision=None,
1479
_hook_master=None, _run_hooks=True):
1498
_hook_master=None, run_hooks=True, possible_transports=None):
1480
1499
"""See Branch.pull.
1482
1501
:param _hook_master: Private parameter - set the branch to
1483
1502
be supplied as the master to push hooks.
1484
:param _run_hooks: Private parameter - allow disabling of
1485
hooks, used when pushing to a master branch.
1503
:param run_hooks: Private parameter - if false, this branch
1504
is being called because it's the master of the primary branch,
1505
so it should not run its hooks.
1487
1507
result = PullResult()
1488
1508
result.source_branch = source
1490
1510
source.lock_read()
1492
1512
result.old_revno, result.old_revid = self.last_revision_info()
1494
self.update_revisions(source, stop_revision)
1495
except DivergedBranches:
1499
if stop_revision is None:
1500
stop_revision = source.last_revision()
1501
self.generate_revision_history(stop_revision)
1502
result.tag_conflicts = source.tags.merge_to(self.tags)
1513
self.update_revisions(source, stop_revision, overwrite=overwrite)
1514
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1503
1515
result.new_revno, result.new_revid = self.last_revision_info()
1504
1516
if _hook_master:
1505
1517
result.master_branch = _hook_master
1519
1531
for l in _locs:
1521
1533
return self.control_files.get(l).read().strip('\n')
1534
except errors.NoSuchFile:
1526
1538
@needs_read_lock
1527
1539
def push(self, target, overwrite=False, stop_revision=None,
1528
_hook_master=None, _run_hooks=True):
1540
_override_hook_source_branch=None):
1529
1541
"""See Branch.push.
1543
This is the basic concrete implementation of push()
1545
:param _override_hook_source_branch: If specified, run
1546
the hooks passing this Branch as the source, rather than self.
1547
This is for use of RemoteBranch, where push is delegated to the
1548
underlying vfs-based Branch.
1550
# TODO: Public option to disable running hooks - should be trivial but
1554
result = self._push_with_bound_branches(target, overwrite,
1556
_override_hook_source_branch=_override_hook_source_branch)
1561
def _push_with_bound_branches(self, target, overwrite,
1563
_override_hook_source_branch=None):
1564
"""Push from self into target, and into target's master if any.
1531
:param _hook_master: Private parameter - set the branch to
1532
be supplied as the master to push hooks.
1533
:param _run_hooks: Private parameter - allow disabling of
1534
hooks, used when pushing to a master branch.
1566
This is on the base BzrBranch class even though it doesn't support
1567
bound branches because the *target* might be bound.
1570
if _override_hook_source_branch:
1571
result.source_branch = _override_hook_source_branch
1572
for hook in Branch.hooks['post_push']:
1575
bound_location = target.get_bound_location()
1576
if bound_location and target.base != bound_location:
1577
# there is a master branch.
1579
# XXX: Why the second check? Is it even supported for a branch to
1580
# be bound to itself? -- mbp 20070507
1581
master_branch = target.get_master_branch()
1582
master_branch.lock_write()
1584
# push into the master from this branch.
1585
self._basic_push(master_branch, overwrite, stop_revision)
1586
# and push into the target branch from this. Note that we push from
1587
# this branch again, because its considered the highest bandwidth
1589
result = self._basic_push(target, overwrite, stop_revision)
1590
result.master_branch = master_branch
1591
result.local_branch = target
1595
master_branch.unlock()
1598
result = self._basic_push(target, overwrite, stop_revision)
1599
# TODO: Why set master_branch and local_branch if there's no
1600
# binding? Maybe cleaner to just leave them unset? -- mbp
1602
result.master_branch = target
1603
result.local_branch = None
1607
def _basic_push(self, target, overwrite, stop_revision):
1608
"""Basic implementation of push without bound branches or hooks.
1610
Must be called with self read locked and target write locked.
1536
1612
result = PushResult()
1537
1613
result.source_branch = self
1538
1614
result.target_branch = target
1615
result.old_revno, result.old_revid = target.last_revision_info()
1541
result.old_revno, result.old_revid = target.last_revision_info()
1543
target.update_revisions(self, stop_revision)
1544
except DivergedBranches:
1548
target.set_revision_history(self.revision_history())
1549
result.tag_conflicts = self.tags.merge_to(target.tags)
1550
result.new_revno, result.new_revid = target.last_revision_info()
1552
result.master_branch = _hook_master
1553
result.local_branch = target
1555
result.master_branch = target
1556
result.local_branch = None
1558
for hook in Branch.hooks['post_push']:
1617
target.update_revisions(self, stop_revision)
1618
except errors.DivergedBranches:
1622
target.set_revision_history(self.revision_history())
1623
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1624
result.new_revno, result.new_revid = target.last_revision_info()
1564
1627
def get_parent(self):
1635
1692
@needs_write_lock
1636
1693
def pull(self, source, overwrite=False, stop_revision=None,
1638
"""Extends branch.pull to be bound branch aware.
1694
run_hooks=True, possible_transports=None):
1695
"""Pull from source into self, updating my master if any.
1640
:param _run_hooks: Private parameter used to force hook running
1641
off during bound branch double-pushing.
1697
:param run_hooks: Private parameter - if false, this branch
1698
is being called because it's the master of the primary branch,
1699
so it should not run its hooks.
1643
1701
bound_location = self.get_bound_location()
1644
1702
master_branch = None
1645
1703
if bound_location and source.base != bound_location:
1646
1704
# not pulling from master, so we need to update master.
1647
master_branch = self.get_master_branch()
1705
master_branch = self.get_master_branch(possible_transports)
1648
1706
master_branch.lock_write()
1650
1708
if master_branch:
1651
1709
# pull from source into master.
1652
1710
master_branch.pull(source, overwrite, stop_revision,
1654
1712
return super(BzrBranch5, self).pull(source, overwrite,
1655
1713
stop_revision, _hook_master=master_branch,
1656
_run_hooks=_run_hooks)
1659
master_branch.unlock()
1662
def push(self, target, overwrite=False, stop_revision=None):
1663
"""Updates branch.push to be bound branch aware."""
1664
bound_location = target.get_bound_location()
1665
master_branch = None
1666
if bound_location and target.base != bound_location:
1667
# not pushing to master, so we need to update master.
1668
master_branch = target.get_master_branch()
1669
master_branch.lock_write()
1672
# push into the master from this branch.
1673
super(BzrBranch5, self).push(master_branch, overwrite,
1674
stop_revision, _run_hooks=False)
1675
# and push into the target branch from this. Note that we push from
1676
# this branch again, because its considered the highest bandwidth
1678
return super(BzrBranch5, self).push(target, overwrite,
1679
stop_revision, _hook_master=master_branch)
1714
run_hooks=run_hooks)
1681
1716
if master_branch:
1682
1717
master_branch.unlock()
1746
1782
# last_rev is not in the other_last_rev history, AND
1747
1783
# other_last_rev is not in our history, and do it without pulling
1748
1784
# history around
1749
last_rev = self.last_revision()
1750
if last_rev is not None:
1753
other_last_rev = other.last_revision()
1754
if other_last_rev is not None:
1755
# neither branch is new, we have to do some work to
1756
# ascertain diversion.
1757
remote_graph = other.repository.get_revision_graph(
1759
local_graph = self.repository.get_revision_graph(last_rev)
1760
if (last_rev not in remote_graph and
1761
other_last_rev not in local_graph):
1762
raise errors.DivergedBranches(self, other)
1765
1785
self.set_bound_location(other.base)
1767
1787
@needs_write_lock
1770
1790
return self.set_bound_location(None)
1772
1792
@needs_write_lock
1793
def update(self, possible_transports=None):
1774
1794
"""Synchronise this branch with the master branch if any.
1776
1796
:return: None or the last_revision that was pivoted out during the
1779
master = self.get_master_branch()
1799
master = self.get_master_branch(possible_transports)
1780
1800
if master is not None:
1781
old_tip = self.last_revision()
1801
old_tip = _mod_revision.ensure_null(self.last_revision())
1782
1802
self.pull(master, overwrite=True)
1783
if old_tip in self.repository.get_ancestry(self.last_revision()):
1803
if self.repository.get_graph().is_ancestor(old_tip,
1804
_mod_revision.ensure_null(self.last_revision())):
1789
class BzrBranchExperimental(BzrBranch5):
1790
"""Bzr experimental branch format
1793
- a revision-history file.
1795
- a lock dir guarding the branch itself
1796
- all of this stored in a branch/ subdirectory
1797
- works with shared repositories.
1798
- a tag dictionary in the branch
1800
This format is new in bzr 0.15, but shouldn't be used for real data,
1803
This class acts as it's own BranchFormat.
1806
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1809
def get_format_string(cls):
1810
"""See BranchFormat.get_format_string()."""
1811
return "Bazaar-NG branch format experimental\n"
1814
def get_format_description(cls):
1815
"""See BranchFormat.get_format_description()."""
1816
return "Experimental branch format"
1819
def get_reference(cls, a_bzrdir):
1820
"""Get the target reference of the branch in a_bzrdir.
1822
format probing must have been completed before calling
1823
this method - it is assumed that the format of the branch
1824
in a_bzrdir is correct.
1826
:param a_bzrdir: The bzrdir to get the branch data from.
1827
:return: None if the branch is not a reference branch.
1832
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1834
branch_transport = a_bzrdir.get_branch_transport(cls)
1835
control_files = lockable_files.LockableFiles(branch_transport,
1836
lock_filename, lock_class)
1837
control_files.create_lock()
1838
control_files.lock_write()
1840
for filename, content in utf8_files:
1841
control_files.put_utf8(filename, content)
1843
control_files.unlock()
1846
def initialize(cls, a_bzrdir):
1847
"""Create a branch of this format in a_bzrdir."""
1848
utf8_files = [('format', cls.get_format_string()),
1849
('revision-history', ''),
1850
('branch-name', ''),
1853
cls._initialize_control_files(a_bzrdir, utf8_files,
1854
'lock', lockdir.LockDir)
1855
return cls.open(a_bzrdir, _found=True)
1858
def open(cls, a_bzrdir, _found=False):
1859
"""Return the branch object for a_bzrdir
1861
_found is a private parameter, do not use it. It is used to indicate
1862
if format probing has already be done.
1865
format = BranchFormat.find_format(a_bzrdir)
1866
assert format.__class__ == cls
1867
transport = a_bzrdir.get_branch_transport(None)
1868
control_files = lockable_files.LockableFiles(transport, 'lock',
1870
return cls(_format=cls,
1871
_control_files=control_files,
1873
_repository=a_bzrdir.find_repository())
1876
def is_supported(cls):
1879
def _make_tags(self):
1880
return BasicTags(self)
1883
def supports_tags(cls):
1887
BranchFormat.register_format(BzrBranchExperimental)
1890
1810
class BzrBranch6(BzrBranch5):
1892
1812
@needs_read_lock
1913
1826
Intended to be called by set_last_revision_info and
1914
1827
_write_revision_history.
1916
if revision_id is None:
1917
revision_id = 'null:'
1829
assert revision_id is not None, "Use NULL_REVISION, not None"
1918
1830
out_string = '%d %s\n' % (revno, revision_id)
1919
1831
self.control_files.put_bytes('last-revision', out_string)
1921
1833
@needs_write_lock
1922
1834
def set_last_revision_info(self, revno, revision_id):
1923
revision_id = osutils.safe_revision_id(revision_id)
1835
revision_id = _mod_revision.ensure_null(revision_id)
1924
1836
if self._get_append_revisions_only():
1925
1837
self._check_history_violation(revision_id)
1926
1838
self._write_last_revision_info(revno, revision_id)
1927
1839
self._clear_cached_state()
1929
1841
def _check_history_violation(self, revision_id):
1930
last_revision = self.last_revision()
1931
if last_revision is None:
1842
last_revision = _mod_revision.ensure_null(self.last_revision())
1843
if _mod_revision.is_null(last_revision):
1933
1845
if last_revision not in self._lefthand_history(revision_id):
1934
1846
raise errors.AppendRevisionsOnlyViolation(self.base)
1958
1870
self._write_last_revision_info(len(history), last_revision)
1960
1872
@needs_write_lock
1961
def append_revision(self, *revision_ids):
1962
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1963
if len(revision_ids) == 0:
1965
prev_revno, prev_revision = self.last_revision_info()
1966
for revision in self.repository.get_revisions(revision_ids):
1967
if prev_revision == _mod_revision.NULL_REVISION:
1968
if revision.parent_ids != []:
1969
raise errors.NotLeftParentDescendant(self, prev_revision,
1970
revision.revision_id)
1972
if revision.parent_ids[0] != prev_revision:
1973
raise errors.NotLeftParentDescendant(self, prev_revision,
1974
revision.revision_id)
1975
prev_revision = revision.revision_id
1976
self.set_last_revision_info(prev_revno + len(revision_ids),
1980
1873
def _set_parent_location(self, url):
1981
1874
"""Set the parent branch"""
1982
1875
self._set_config_location('parent_location', url, make_relative=True)
2047
1941
:param revision_id: The revision-id to truncate history at. May
2048
1942
be None to copy complete history.
1944
source_revno, source_revision_id = self.last_revision_info()
2050
1945
if revision_id is None:
2051
revno, revision_id = self.last_revision_info()
1946
revno, revision_id = source_revno, source_revision_id
1947
elif source_revision_id == revision_id:
1948
# we know the revno without needing to walk all of history
1949
revno = source_revno
2053
revno = self.revision_id_to_revno(revision_id)
1951
# To figure out the revno for a random revision, we need to build
1952
# the revision history, and count its length.
1953
# We don't care about the order, just how long it is.
1954
# Alternatively, we could start at the current location, and count
1955
# backwards. But there is no guarantee that we will find it since
1956
# it may be a merged revision.
1957
revno = len(list(self.repository.iter_reverse_revision_history(
2054
1959
destination.set_last_revision_info(revno, revision_id)
2056
1961
def _make_tags(self):
2057
1962
return BasicTags(self)
2060
class BranchTestProviderAdapter(object):
2061
"""A tool to generate a suite testing multiple branch formats at once.
2063
This is done by copying the test once for each transport and injecting
2064
the transport_server, transport_readonly_server, and branch_format
2065
classes into each copy. Each copy is also given a new id() to make it
2069
def __init__(self, transport_server, transport_readonly_server, formats,
2070
vfs_transport_factory=None):
2071
self._transport_server = transport_server
2072
self._transport_readonly_server = transport_readonly_server
2073
self._formats = formats
2075
def adapt(self, test):
2076
result = TestSuite()
2077
for branch_format, bzrdir_format in self._formats:
2078
new_test = deepcopy(test)
2079
new_test.transport_server = self._transport_server
2080
new_test.transport_readonly_server = self._transport_readonly_server
2081
new_test.bzrdir_format = bzrdir_format
2082
new_test.branch_format = branch_format
2083
def make_new_test_id():
2084
# the format can be either a class or an instance
2085
name = getattr(branch_format, '__name__',
2086
branch_format.__class__.__name__)
2087
new_id = "%s(%s)" % (new_test.id(), name)
2088
return lambda: new_id
2089
new_test.id = make_new_test_id()
2090
result.addTest(new_test)
1965
def generate_revision_history(self, revision_id, last_rev=None,
1967
"""See BzrBranch5.generate_revision_history"""
1968
history = self._lefthand_history(revision_id, last_rev, other_branch)
1969
revno = len(history)
1970
self.set_last_revision_info(revno, revision_id)
2094
1973
######################################################################