317
319
The delta is relative to its mainline predecessor, or the
318
320
empty tree for revision 1.
320
assert isinstance(revno, int)
321
322
rh = self.revision_history()
322
323
if not (1 <= revno <= len(rh)):
323
324
raise errors.InvalidRevisionNumber(revno)
324
325
return self.repository.get_revision_delta(rh[revno-1])
326
@deprecated_method(zero_sixteen)
327
def get_root_id(self):
328
"""Return the id of this branches root
330
Deprecated: branches don't have root ids-- trees do.
331
Use basis_tree().get_root_id() instead.
333
raise NotImplementedError(self.get_root_id)
335
327
def print_file(self, file, revision_id):
336
328
"""Print `file` to stdout."""
337
329
raise NotImplementedError(self.print_file)
451
444
if stop_revision is None:
452
445
stop_revision = other_len
454
assert isinstance(stop_revision, int)
455
447
if stop_revision > other_len:
456
448
raise errors.NoSuchRevision(self, stop_revision)
457
449
return other_history[self_len:stop_revision]
459
def update_revisions(self, other, stop_revision=None):
452
def update_revisions(self, other, stop_revision=None, overwrite=False,
460
454
"""Pull in new perfect-fit revisions.
462
456
:param other: Another Branch to pull from
463
457
:param stop_revision: Updated until the given revision
458
:param overwrite: Always set the branch pointer, rather than checking
459
to see if it is a proper descendant.
460
:param graph: A Graph object that can be used to query history
461
information. This can be None.
466
raise NotImplementedError(self.update_revisions)
466
other_revno, other_last_revision = other.last_revision_info()
467
stop_revno = None # unknown
468
if stop_revision is None:
469
stop_revision = other_last_revision
470
if _mod_revision.is_null(stop_revision):
471
# if there are no commits, we're done.
473
stop_revno = other_revno
475
# what's the current last revision, before we fetch [and change it
477
last_rev = _mod_revision.ensure_null(self.last_revision())
478
# we fetch here so that we don't process data twice in the common
479
# case of having something to pull, and so that the check for
480
# already merged can operate on the just fetched graph, which will
481
# be cached in memory.
482
self.fetch(other, stop_revision)
483
# Check to see if one is an ancestor of the other
486
graph = self.repository.get_graph()
487
heads = graph.heads([stop_revision, last_rev])
488
if heads == set([last_rev]):
489
# The current revision is a decendent of the target,
492
elif heads == set([stop_revision, last_rev]):
493
# These branches have diverged
494
raise errors.DivergedBranches(self, other)
495
elif heads != set([stop_revision]):
496
raise AssertionError("invalid heads: %r" % heads)
497
if stop_revno is None:
499
graph = self.repository.get_graph()
500
this_revno, this_last_revision = self.last_revision_info()
501
stop_revno = graph.find_distance_to_null(stop_revision,
502
[(other_last_revision, other_revno),
503
(this_last_revision, this_revno)])
504
self.set_last_revision_info(stop_revno, stop_revision)
468
510
def revision_id_to_revno(self, revision_id):
469
511
"""Given a revision id, return its revno"""
917
959
control_files.create_lock()
918
960
control_files.lock_write()
920
control_files.put_utf8('format', self.get_format_string())
962
utf8_files += [('format', self.get_format_string())]
922
for file, content in utf8_files:
923
control_files.put_utf8(file, content)
964
for (filename, content) in utf8_files:
965
branch_transport.put_bytes(
967
mode=a_bzrdir._get_file_mode())
925
969
control_files.unlock()
926
970
return self.open(a_bzrdir, _found=True)
966
1009
"""True if this format supports tags stored in the branch"""
967
1010
return False # by default
969
# XXX: Probably doesn't really belong here -- mbp 20070212
970
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
972
branch_transport = a_bzrdir.get_branch_transport(self)
973
control_files = lockable_files.LockableFiles(branch_transport,
974
lock_filename, lock_class)
975
control_files.create_lock()
976
control_files.lock_write()
978
for filename, content in utf8_files:
979
control_files.put_utf8(filename, content)
981
control_files.unlock()
984
1013
class BranchHooks(Hooks):
985
1014
"""A dictionary mapping hook name to a list of callables for branch hooks.
1329
1364
Note that it's "local" in the context of the filesystem; it doesn't
1330
1365
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1331
1366
it's writable, and can be accessed via the normal filesystem API.
1368
:ivar _transport: Transport for file operations on this branch's
1369
control files, typically pointing to the .bzr/branch directory.
1370
:ivar repository: Repository for this branch.
1371
:ivar base: The url of the base directory for this branch; the one
1372
containing the .bzr directory.
1334
1375
def __init__(self, _format=None,
1339
1380
raise ValueError('a_bzrdir must be supplied')
1341
1382
self.bzrdir = a_bzrdir
1342
# self._transport used to point to the directory containing the
1343
# control directory, but was not used - now it's just the transport
1344
# for the branch control files. mbp 20070212
1345
1383
self._base = self.bzrdir.transport.clone('..').base
1384
# XXX: We should be able to just do
1385
# self.base = self.bzrdir.root_transport.base
1386
# but this does not quite work yet -- mbp 20080522
1346
1387
self._format = _format
1347
1388
if _control_files is None:
1348
1389
raise ValueError('BzrBranch _control_files is None')
1362
1403
base = property(_get_base, doc="The URL for the root of this branch.")
1405
@deprecated_method(deprecated_in((0, 16, 0)))
1364
1406
def abspath(self, name):
1365
1407
"""See Branch.abspath."""
1366
return self.control_files._transport.abspath(name)
1369
@deprecated_method(zero_sixteen)
1371
def get_root_id(self):
1372
"""See Branch.get_root_id."""
1373
tree = self.repository.revision_tree(self.last_revision())
1374
return tree.get_root_id()
1408
return self._transport.abspath(name)
1376
1410
def is_locked(self):
1377
1411
return self.control_files.is_locked()
1423
1457
This performs the actual writing to disk.
1424
1458
It is intended to be called by BzrBranch5.set_revision_history."""
1425
self.control_files.put_bytes(
1426
'revision-history', '\n'.join(history))
1459
self._transport.put_bytes(
1460
'revision-history', '\n'.join(history),
1461
mode=self.bzrdir._get_file_mode())
1428
1463
@needs_write_lock
1429
1464
def set_revision_history(self, rev_history):
1462
1497
revision_id = _mod_revision.ensure_null(revision_id)
1463
1498
old_revno, old_revid = self.last_revision_info()
1499
# this old format stores the full history, but this api doesn't
1500
# provide it, so we must generate, and might as well check it's
1464
1502
history = self._lefthand_history(revision_id)
1465
assert len(history) == revno, '%d != %d' % (len(history), revno)
1503
if len(history) != revno:
1504
raise AssertionError('%d != %d' % (len(history), revno))
1466
1505
self.set_revision_history(history)
1467
1506
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1469
1508
def _gen_revision_history(self):
1470
history = self.control_files.get('revision-history').read().split('\n')
1509
history = self._transport.get_bytes('revision-history').split('\n')
1471
1510
if history[-1:] == ['']:
1472
1511
# There shouldn't be a trailing newline, but just in case.
1512
1551
self.set_revision_history(self._lefthand_history(revision_id,
1513
1552
last_rev, other_branch))
1516
def update_revisions(self, other, stop_revision=None, overwrite=False):
1517
"""See Branch.update_revisions."""
1520
other_last_revno, other_last_revision = other.last_revision_info()
1521
if stop_revision is None:
1522
stop_revision = other_last_revision
1523
if _mod_revision.is_null(stop_revision):
1524
# if there are no commits, we're done.
1526
# whats the current last revision, before we fetch [and change it
1528
last_rev = _mod_revision.ensure_null(self.last_revision())
1529
# we fetch here so that we don't process data twice in the common
1530
# case of having something to pull, and so that the check for
1531
# already merged can operate on the just fetched graph, which will
1532
# be cached in memory.
1533
self.fetch(other, stop_revision)
1534
# Check to see if one is an ancestor of the other
1536
heads = self.repository.get_graph().heads([stop_revision,
1538
if heads == set([last_rev]):
1539
# The current revision is a decendent of the target,
1542
elif heads == set([stop_revision, last_rev]):
1543
# These branches have diverged
1544
raise errors.DivergedBranches(self, other)
1545
assert heads == set([stop_revision])
1546
if other_last_revision == stop_revision:
1547
self.set_last_revision_info(other_last_revno,
1548
other_last_revision)
1550
# TODO: jam 2007-11-29 Is there a way to determine the
1551
# revno without searching all of history??
1553
self.generate_revision_history(stop_revision)
1555
self.generate_revision_history(stop_revision,
1556
last_rev=last_rev, other_branch=other)
1560
1554
def basis_tree(self):
1561
1555
"""See Branch.basis_tree."""
1562
1556
return self.repository.revision_tree(self.last_revision())
1577
1571
result.target_branch = self
1578
1572
source.lock_read()
1574
# We assume that during 'pull' the local repository is closer than
1576
graph = self.repository.get_graph(source.repository)
1580
1577
result.old_revno, result.old_revid = self.last_revision_info()
1581
self.update_revisions(source, stop_revision, overwrite=overwrite)
1578
self.update_revisions(source, stop_revision, overwrite=overwrite,
1582
1580
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1583
1581
result.new_revno, result.new_revid = self.last_revision_info()
1584
1582
if _hook_master:
1681
1679
result.source_branch = self
1682
1680
result.target_branch = target
1683
1681
result.old_revno, result.old_revid = target.last_revision_info()
1685
target.update_revisions(self, stop_revision)
1686
except errors.DivergedBranches:
1690
target.set_revision_history(self.revision_history())
1683
# We assume that during 'push' this repository is closer than
1685
graph = self.repository.get_graph(target.repository)
1686
target.update_revisions(self, stop_revision, overwrite=overwrite,
1691
1688
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1692
1689
result.new_revno, result.new_revid = target.last_revision_info()
1695
1692
def get_parent(self):
1696
1693
"""See Branch.get_parent."""
1698
assert self.base[-1] == '/'
1699
1694
parent = self._get_parent_location()
1700
1695
if parent is None:
1735
1730
def _set_parent_location(self, url):
1736
1731
if url is None:
1737
self.control_files._transport.delete('parent')
1732
self._transport.delete('parent')
1739
assert isinstance(url, str)
1740
self.control_files.put_bytes('parent', url + '\n')
1734
self._transport.put_bytes('parent', url + '\n',
1735
mode=self.bzrdir._get_file_mode())
1743
1738
class BzrBranch5(BzrBranch):
1912
1908
Intended to be called by set_last_revision_info and
1913
1909
_write_revision_history.
1915
assert revision_id is not None, "Use NULL_REVISION, not None"
1911
revision_id = _mod_revision.ensure_null(revision_id)
1916
1912
out_string = '%d %s\n' % (revno, revision_id)
1917
self.control_files.put_bytes('last-revision', out_string)
1913
self._transport.put_bytes('last-revision', out_string,
1914
mode=self.bzrdir._get_file_mode())
1919
1916
@needs_write_lock
1920
1917
def set_last_revision_info(self, revno, revision_id):
2145
2140
:ivar old_revid: Tip revision id before pull.
2146
2141
:ivar new_revid: Tip revision id after pull.
2147
2142
:ivar source_branch: Source (local) branch object.
2148
:ivar master_branch: Master branch of the target, or None.
2143
:ivar master_branch: Master branch of the target, or the target if no
2145
:ivar local_branch: target branch if there is a Master, else None
2149
2146
:ivar target_branch: Target/destination branch object.
2147
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2152
2150
def __int__(self):
2225
2223
new_branch.tags._set_tag_dict({})
2227
2225
# Copying done; now update target format
2228
new_branch.control_files.put_utf8('format',
2229
format.get_format_string())
2226
new_branch._transport.put_bytes('format',
2227
format.get_format_string(),
2228
mode=new_branch.bzrdir._get_file_mode())
2231
2230
# Clean up old files
2232
new_branch.control_files._transport.delete('revision-history')
2231
new_branch._transport.delete('revision-history')
2234
2233
branch.set_parent(None)
2235
2234
except errors.NoSuchFile: