15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
18
22
from copy import deepcopy
19
from cStringIO import StringIO
20
23
from unittest import TestSuite
21
24
from warnings import warn
24
27
from bzrlib import (
34
revision as _mod_revision,
35
from bzrlib.config import TreeConfig
40
from bzrlib.config import BranchConfig, TreeConfig
41
from bzrlib.lockable_files import LockableFiles, TransportLock
36
44
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
import bzrlib.errors as errors
38
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
39
HistoryMissing, InvalidRevisionId,
40
InvalidRevisionNumber, LockError, NoSuchFile,
45
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
46
HistoryMissing, InvalidRevisionId,
47
InvalidRevisionNumber, LockError, NoSuchFile,
41
48
NoSuchRevision, NoWorkingTree, NotVersionedError,
42
NotBranchError, UninitializableFormat,
43
UnlistableStore, UnlistableBranch,
49
NotBranchError, UninitializableFormat,
50
UnlistableStore, UnlistableBranch,
45
from bzrlib.lockable_files import LockableFiles, TransportLock
46
52
from bzrlib.symbol_versioning import (deprecated_function,
48
54
DEPRECATED_PARAMETER,
138
144
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
146
@deprecated_function(zero_eight)
140
147
def setup_caching(self, cache_root):
141
148
"""Subclasses that care about caching should override this, and set
142
149
up cached stores located under cache_root.
151
NOTE: This is unused.
144
# seems to be unused, 2006-01-13 mbp
145
warn('%s is deprecated' % self.setup_caching)
146
self.cache_root = cache_root
148
155
def get_config(self):
149
return bzrlib.config.BranchConfig(self)
156
return BranchConfig(self)
151
158
def _get_nick(self):
152
159
return self.get_config().get_nickname()
157
164
nick = property(_get_nick, _set_nick)
159
166
def is_locked(self):
160
raise NotImplementedError('is_locked is abstract')
167
raise NotImplementedError(self.is_locked)
162
169
def lock_write(self):
163
raise NotImplementedError('lock_write is abstract')
170
raise NotImplementedError(self.lock_write)
165
172
def lock_read(self):
166
raise NotImplementedError('lock_read is abstract')
173
raise NotImplementedError(self.lock_read)
168
175
def unlock(self):
169
raise NotImplementedError('unlock is abstract')
176
raise NotImplementedError(self.unlock)
171
178
def peek_lock_mode(self):
172
179
"""Return lock mode for the Branch: 'r', 'w' or None"""
173
180
raise NotImplementedError(self.peek_lock_mode)
175
182
def get_physical_lock_status(self):
176
raise NotImplementedError('get_physical_lock_status is abstract')
183
raise NotImplementedError(self.get_physical_lock_status)
178
185
def abspath(self, name):
179
186
"""Return absolute filename for something in the branch
220
227
last_revision = from_history[-1]
222
229
# no history in the source branch
223
last_revision = revision.NULL_REVISION
230
last_revision = _mod_revision.NULL_REVISION
224
231
return self.repository.fetch(from_branch.repository,
225
232
revision_id=last_revision,
279
286
def get_root_id(self):
280
287
"""Return the id of this branches root"""
281
raise NotImplementedError('get_root_id is abstract')
288
raise NotImplementedError(self.get_root_id)
283
290
def print_file(self, file, revision_id):
284
291
"""Print `file` to stdout."""
285
raise NotImplementedError('print_file is abstract')
292
raise NotImplementedError(self.print_file)
287
294
def append_revision(self, *revision_ids):
288
raise NotImplementedError('append_revision is abstract')
295
raise NotImplementedError(self.append_revision)
290
297
def set_revision_history(self, rev_history):
291
raise NotImplementedError('set_revision_history is abstract')
298
raise NotImplementedError(self.set_revision_history)
293
300
def revision_history(self):
294
301
"""Return sequence of revision hashes on to this branch."""
295
raise NotImplementedError('revision_history is abstract')
302
raise NotImplementedError(self.revision_history)
298
305
"""Return current revision number for this branch.
344
351
:param stop_revision: Updated until the given revision
347
raise NotImplementedError('update_revisions is abstract')
354
raise NotImplementedError(self.update_revisions)
349
356
def revision_id_to_revno(self, revision_id):
350
357
"""Given a revision id, return its revno"""
363
370
if history is None:
364
371
history = self.revision_history()
365
elif revno <= 0 or revno > len(history):
372
if revno <= 0 or revno > len(history):
366
373
raise bzrlib.errors.NoSuchRevision(self, revno)
367
374
return history[revno - 1]
369
376
def pull(self, source, overwrite=False, stop_revision=None):
370
raise NotImplementedError('pull is abstract')
377
raise NotImplementedError(self.pull)
372
379
def basis_tree(self):
373
380
"""Return `Tree` object for last revision."""
426
433
def get_push_location(self):
427
434
"""Return the None or the location to push this branch to."""
428
raise NotImplementedError('get_push_location is abstract')
435
raise NotImplementedError(self.get_push_location)
430
437
def set_push_location(self, location):
431
438
"""Set a new push location for this branch."""
432
raise NotImplementedError('set_push_location is abstract')
439
raise NotImplementedError(self.set_push_location)
434
441
def set_parent(self, url):
435
raise NotImplementedError('set_parent is abstract')
442
raise NotImplementedError(self.set_parent)
437
444
@needs_write_lock
438
445
def update(self):
579
586
mainline_parent_id = revision_id
580
587
return BranchCheckResult(self)
589
def _get_checkout_format(self):
590
"""Return the most suitable metadir for a checkout of this branch.
591
Weaves are used if this branch's repostory uses weaves.
593
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
594
from bzrlib import repository
595
format = bzrdir.BzrDirMetaFormat1()
596
format.repository_format = repository.RepositoryFormat7()
598
format = self.repository.bzrdir.cloning_metadir()
582
601
def create_checkout(self, to_location, revision_id=None,
583
602
lightweight=False):
584
603
"""Create a checkout of a branch.
589
608
produce a bound branch (heavyweight checkout)
590
609
:return: The tree of the created checkout
611
t = transport.get_transport(to_location)
614
except errors.FileExists:
593
t = transport.get_transport(to_location)
596
except errors.FileExists:
598
617
checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
599
618
BranchReferenceFormat().initialize(checkout, self)
620
format = self._get_checkout_format()
601
621
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
602
to_location, force_new_tree=False)
622
to_location, force_new_tree=False, format=format)
603
623
checkout = checkout_branch.bzrdir
604
624
checkout_branch.bind(self)
605
if revision_id is not None:
606
rh = checkout_branch.revision_history()
607
new_rh = rh[:rh.index(revision_id) + 1]
608
checkout_branch.set_revision_history(new_rh)
625
# pull up to the specified revision_id to set the initial
626
# branch tip correctly, and seed it with history.
627
checkout_branch.pull(self, stop_revision=revision_id)
609
628
return checkout.create_workingtree(revision_id)
715
734
utf8_files = [('revision-history', ''),
716
735
('branch-name', ''),
718
control_files = LockableFiles(branch_transport, 'branch-lock',
737
control_files = lockable_files.LockableFiles(branch_transport,
738
'branch-lock', lockable_files.TransportLock)
720
739
control_files.create_lock()
721
740
control_files.lock_write()
776
795
utf8_files = [('revision-history', ''),
777
796
('branch-name', ''),
779
control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
798
control_files = lockable_files.LockableFiles(branch_transport, 'lock',
780
800
control_files.create_lock()
781
801
control_files.lock_write()
782
802
control_files.put_utf8('format', self.get_format_string())
801
821
format = BranchFormat.find_format(a_bzrdir)
802
822
assert format.__class__ == self.__class__
803
823
transport = a_bzrdir.get_branch_transport(None)
804
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
824
control_files = lockable_files.LockableFiles(transport, 'lock',
805
826
return BzrBranch5(_format=self,
806
827
_control_files=control_files,
807
828
a_bzrdir=a_bzrdir,
838
859
raise errors.UninitializableFormat(self)
839
860
mutter('creating branch reference in %s', a_bzrdir.transport.base)
840
861
branch_transport = a_bzrdir.get_branch_transport(self)
841
# FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
842
branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
843
branch_transport.put('format', StringIO(self.get_format_string()))
862
branch_transport.put_bytes('location',
863
target_branch.bzrdir.root_transport.base)
864
branch_transport.put_bytes('format', self.get_format_string())
844
865
return self.open(a_bzrdir, _found=True)
846
867
def __init__(self):
958
979
__repr__ = __str__
961
# TODO: It might be best to do this somewhere else,
962
# but it is nice for a Branch object to automatically
963
# cache it's information.
964
# Alternatively, we could have the Transport objects cache requests
965
# See the earlier discussion about how major objects (like Branch)
966
# should never expect their __del__ function to run.
967
# XXX: cache_root seems to be unused, 2006-01-13 mbp
968
if hasattr(self, 'cache_root') and self.cache_root is not None:
970
osutils.rmtree(self.cache_root)
973
self.cache_root = None
975
981
def _get_base(self):
976
982
return self._base
1100
1106
transaction = self.get_transaction()
1101
1107
history = transaction.map.find_revision_history()
1102
1108
if history is not None:
1103
mutter("cache hit for revision-history in %s", self)
1109
# mutter("cache hit for revision-history in %s", self)
1104
1110
return list(history)
1105
history = [l.rstrip('\r\n') for l in
1106
self.control_files.get_utf8('revision-history').readlines()]
1111
decode_utf8 = cache_utf8.decode
1112
history = [decode_utf8(l.rstrip('\r\n')) for l in
1113
self.control_files.get('revision-history').readlines()]
1107
1114
transaction.map.add_revision_history(history)
1108
1115
# this call is disabled because revision_history is
1109
1116
# not really an object yet, and the transaction is for objects.
1129
1136
# make a new revision history from the graph
1130
1137
current_rev_id = revision_id
1131
1138
new_history = []
1132
while current_rev_id not in (None, revision.NULL_REVISION):
1139
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1133
1140
new_history.append(current_rev_id)
1134
1141
current_rev_id_parents = stop_graph[current_rev_id]
1246
1253
"use bzrlib.urlutils.escape")
1248
1255
url = urlutils.relative_url(self.base, url)
1249
self.control_files.put('parent', url + '\n')
1256
self.control_files.put('parent', StringIO(url + '\n'))
1251
1258
@deprecated_function(zero_nine)
1252
1259
def tree_config(self):
1327
1334
@needs_write_lock
1328
1335
def bind(self, other):
1329
"""Bind the local branch the other branch.
1336
"""Bind this branch to the branch other.
1338
This does not push or pull data between the branches, though it does
1339
check for divergence to raise an error when the branches are not
1340
either the same, or one a prefix of the other. That behaviour may not
1341
be useful, so that check may be removed in future.
1331
1343
:param other: The branch to bind to
1332
1344
:type other: Branch
1338
1350
# but binding itself may not be.
1339
1351
# Since we *have* to check at commit time, we don't
1340
1352
# *need* to check here
1343
# we are now equal to or a suffix of other.
1345
# Since we have 'pulled' from the remote location,
1346
# now we should try to pull in the opposite direction
1347
# in case the local tree has more revisions than the
1349
# There may be a different check you could do here
1350
# rather than actually trying to install revisions remotely.
1351
# TODO: capture an exception which indicates the remote branch
1353
# If it is up-to-date, this probably should not be a failure
1355
# lock other for write so the revision-history syncing cannot race
1359
# if this does not error, other now has the same last rev we do
1360
# it can only error if the pull from other was concurrent with
1361
# a commit to other from someone else.
1363
# until we ditch revision-history, we need to sync them up:
1364
self.set_revision_history(other.revision_history())
1365
# now other and self are up to date with each other and have the
1366
# same revision-history.
1354
# we want to raise diverged if:
1355
# last_rev is not in the other_last_rev history, AND
1356
# other_last_rev is not in our history, and do it without pulling
1358
last_rev = self.last_revision()
1359
if last_rev is not None:
1362
other_last_rev = other.last_revision()
1363
if other_last_rev is not None:
1364
# neither branch is new, we have to do some work to
1365
# ascertain diversion.
1366
remote_graph = other.repository.get_revision_graph(
1368
local_graph = self.repository.get_revision_graph(last_rev)
1369
if (last_rev not in remote_graph and
1370
other_last_rev not in local_graph):
1371
raise errors.DivergedBranches(self, other)
1370
1374
self.set_bound_location(other.base)
1372
1376
@needs_write_lock