442
448
raise errors.NoSuchRevision(self, stop_revision)
443
449
return other_history[self_len:stop_revision]
445
def update_revisions(self, other, stop_revision=None):
452
def update_revisions(self, other, stop_revision=None, overwrite=False,
446
454
"""Pull in new perfect-fit revisions.
448
456
:param other: Another Branch to pull from
449
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.
452
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)
454
510
def revision_id_to_revno(self, revision_id):
455
511
"""Given a revision id, return its revno"""
903
959
control_files.create_lock()
904
960
control_files.lock_write()
906
control_files.put_utf8('format', self.get_format_string())
962
utf8_files += [('format', self.get_format_string())]
908
for file, content in utf8_files:
909
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())
911
969
control_files.unlock()
912
970
return self.open(a_bzrdir, _found=True)
951
1009
"""True if this format supports tags stored in the branch"""
952
1010
return False # by default
954
# XXX: Probably doesn't really belong here -- mbp 20070212
955
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
957
branch_transport = a_bzrdir.get_branch_transport(self)
958
control_files = lockable_files.LockableFiles(branch_transport,
959
lock_filename, lock_class)
960
control_files.create_lock()
961
control_files.lock_write()
963
for filename, content in utf8_files:
964
control_files.put_utf8(filename, content)
966
control_files.unlock()
969
1013
class BranchHooks(Hooks):
970
1014
"""A dictionary mapping hook name to a list of callables for branch hooks.
1320
1364
Note that it's "local" in the context of the filesystem; it doesn't
1321
1365
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1322
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.
1325
1375
def __init__(self, _format=None,
1330
1380
raise ValueError('a_bzrdir must be supplied')
1332
1382
self.bzrdir = a_bzrdir
1333
# self._transport used to point to the directory containing the
1334
# control directory, but was not used - now it's just the transport
1335
# for the branch control files. mbp 20070212
1336
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
1337
1387
self._format = _format
1338
1388
if _control_files is None:
1339
1389
raise ValueError('BzrBranch _control_files is None')
1406
1457
This performs the actual writing to disk.
1407
1458
It is intended to be called by BzrBranch5.set_revision_history."""
1408
self.control_files.put_bytes(
1409
'revision-history', '\n'.join(history))
1459
self._transport.put_bytes(
1460
'revision-history', '\n'.join(history),
1461
mode=self.bzrdir._get_file_mode())
1411
1463
@needs_write_lock
1412
1464
def set_revision_history(self, rev_history):
1504
1556
self.set_revision_history(self._lefthand_history(revision_id,
1505
1557
last_rev, other_branch))
1508
def update_revisions(self, other, stop_revision=None, overwrite=False):
1509
"""See Branch.update_revisions."""
1512
other_last_revno, other_last_revision = other.last_revision_info()
1513
if stop_revision is None:
1514
stop_revision = other_last_revision
1515
if _mod_revision.is_null(stop_revision):
1516
# if there are no commits, we're done.
1518
# whats the current last revision, before we fetch [and change it
1520
last_rev = _mod_revision.ensure_null(self.last_revision())
1521
# we fetch here so that we don't process data twice in the common
1522
# case of having something to pull, and so that the check for
1523
# already merged can operate on the just fetched graph, which will
1524
# be cached in memory.
1525
self.fetch(other, stop_revision)
1526
# Check to see if one is an ancestor of the other
1528
heads = self.repository.get_graph().heads([stop_revision,
1530
if heads == set([last_rev]):
1531
# The current revision is a decendent of the target,
1534
elif heads == set([stop_revision, last_rev]):
1535
# These branches have diverged
1536
raise errors.DivergedBranches(self, other)
1537
elif heads != set([stop_revision]):
1538
raise AssertionError("invalid heads: %r" % heads)
1539
if other_last_revision == stop_revision:
1540
self.set_last_revision_info(other_last_revno,
1541
other_last_revision)
1543
# TODO: jam 2007-11-29 Is there a way to determine the
1544
# revno without searching all of history??
1546
self.generate_revision_history(stop_revision)
1548
self.generate_revision_history(stop_revision,
1549
last_rev=last_rev, other_branch=other)
1553
1559
def basis_tree(self):
1554
1560
"""See Branch.basis_tree."""
1555
1561
return self.repository.revision_tree(self.last_revision())
1570
1576
result.target_branch = self
1571
1577
source.lock_read()
1579
# We assume that during 'pull' the local repository is closer than
1581
graph = self.repository.get_graph(source.repository)
1573
1582
result.old_revno, result.old_revid = self.last_revision_info()
1574
self.update_revisions(source, stop_revision, overwrite=overwrite)
1583
self.update_revisions(source, stop_revision, overwrite=overwrite,
1575
1585
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1576
1586
result.new_revno, result.new_revid = self.last_revision_info()
1577
1587
if _hook_master:
1674
1684
result.source_branch = self
1675
1685
result.target_branch = target
1676
1686
result.old_revno, result.old_revid = target.last_revision_info()
1678
target.update_revisions(self, stop_revision)
1679
except errors.DivergedBranches:
1683
target.set_revision_history(self.revision_history())
1688
# We assume that during 'push' this repository is closer than
1690
graph = self.repository.get_graph(target.repository)
1691
target.update_revisions(self, stop_revision, overwrite=overwrite,
1684
1693
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1685
1694
result.new_revno, result.new_revid = target.last_revision_info()
1726
1735
def _set_parent_location(self, url):
1727
1736
if url is None:
1728
self.control_files._transport.delete('parent')
1737
self._transport.delete('parent')
1730
self.control_files.put_bytes('parent', url + '\n')
1739
self._transport.put_bytes('parent', url + '\n',
1740
mode=self.bzrdir._get_file_mode())
1733
1743
class BzrBranch5(BzrBranch):
1807
1817
:param location: URL to the target branch
1810
self.control_files.put_utf8('bound', location+'\n')
1820
self._transport.put_bytes('bound', location+'\n',
1821
mode=self.bzrdir._get_file_mode())
1813
self.control_files._transport.delete('bound')
1824
self._transport.delete('bound')
1814
1825
except errors.NoSuchFile:
1905
1916
revision_id = _mod_revision.ensure_null(revision_id)
1906
1917
out_string = '%d %s\n' % (revno, revision_id)
1907
self.control_files.put_bytes('last-revision', out_string)
1918
self._transport.put_bytes('last-revision', out_string,
1919
mode=self.bzrdir._get_file_mode())
1909
1921
@needs_write_lock
1910
1922
def set_last_revision_info(self, revno, revision_id):
2133
2145
:ivar old_revid: Tip revision id before pull.
2134
2146
:ivar new_revid: Tip revision id after pull.
2135
2147
:ivar source_branch: Source (local) branch object.
2136
:ivar master_branch: Master branch of the target, or None.
2148
:ivar master_branch: Master branch of the target, or the target if no
2150
:ivar local_branch: target branch if there is a Master, else None
2137
2151
:ivar target_branch: Target/destination branch object.
2152
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2140
2155
def __int__(self):
2213
2228
new_branch.tags._set_tag_dict({})
2215
2230
# Copying done; now update target format
2216
new_branch.control_files.put_utf8('format',
2217
format.get_format_string())
2231
new_branch._transport.put_bytes('format',
2232
format.get_format_string(),
2233
mode=new_branch.bzrdir._get_file_mode())
2219
2235
# Clean up old files
2220
new_branch.control_files._transport.delete('revision-history')
2236
new_branch._transport.delete('revision-history')
2222
2238
branch.set_parent(None)
2223
2239
except errors.NoSuchFile: