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
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
28
from bzrlib.config import TreeConfig
30
config as _mod_config,
35
revision as _mod_revision,
41
from bzrlib.config import BranchConfig, TreeConfig
42
from bzrlib.lockable_files import LockableFiles, TransportLock
29
45
from bzrlib.decorators import needs_read_lock, needs_write_lock
30
import bzrlib.errors as errors
31
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
32
HistoryMissing, InvalidRevisionId,
33
InvalidRevisionNumber, LockError, NoSuchFile,
46
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
47
HistoryMissing, InvalidRevisionId,
48
InvalidRevisionNumber, LockError, NoSuchFile,
34
49
NoSuchRevision, NoWorkingTree, NotVersionedError,
35
NotBranchError, UninitializableFormat,
36
UnlistableStore, UnlistableBranch,
50
NotBranchError, UninitializableFormat,
51
UnlistableStore, UnlistableBranch,
38
from bzrlib.lockable_files import LockableFiles, TransportLock
39
53
from bzrlib.symbol_versioning import (deprecated_function,
41
55
DEPRECATED_PARAMETER,
131
145
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
147
@deprecated_function(zero_eight)
133
148
def setup_caching(self, cache_root):
134
149
"""Subclasses that care about caching should override this, and set
135
150
up cached stores located under cache_root.
152
NOTE: This is unused.
137
# seems to be unused, 2006-01-13 mbp
138
warn('%s is deprecated' % self.setup_caching)
139
self.cache_root = cache_root
141
156
def get_config(self):
142
return bzrlib.config.BranchConfig(self)
157
return BranchConfig(self)
144
159
def _get_nick(self):
145
160
return self.get_config().get_nickname()
150
165
nick = property(_get_nick, _set_nick)
152
167
def is_locked(self):
153
raise NotImplementedError('is_locked is abstract')
168
raise NotImplementedError(self.is_locked)
155
170
def lock_write(self):
156
raise NotImplementedError('lock_write is abstract')
171
raise NotImplementedError(self.lock_write)
158
173
def lock_read(self):
159
raise NotImplementedError('lock_read is abstract')
174
raise NotImplementedError(self.lock_read)
161
176
def unlock(self):
162
raise NotImplementedError('unlock is abstract')
177
raise NotImplementedError(self.unlock)
164
179
def peek_lock_mode(self):
165
180
"""Return lock mode for the Branch: 'r', 'w' or None"""
166
181
raise NotImplementedError(self.peek_lock_mode)
168
183
def get_physical_lock_status(self):
169
raise NotImplementedError('get_physical_lock_status is abstract')
184
raise NotImplementedError(self.get_physical_lock_status)
171
186
def abspath(self, name):
172
187
"""Return absolute filename for something in the branch
213
228
last_revision = from_history[-1]
215
230
# no history in the source branch
216
last_revision = revision.NULL_REVISION
231
last_revision = _mod_revision.NULL_REVISION
217
232
return self.repository.fetch(from_branch.repository,
218
233
revision_id=last_revision,
272
287
def get_root_id(self):
273
288
"""Return the id of this branches root"""
274
raise NotImplementedError('get_root_id is abstract')
289
raise NotImplementedError(self.get_root_id)
276
291
def print_file(self, file, revision_id):
277
292
"""Print `file` to stdout."""
278
raise NotImplementedError('print_file is abstract')
293
raise NotImplementedError(self.print_file)
280
295
def append_revision(self, *revision_ids):
281
raise NotImplementedError('append_revision is abstract')
296
raise NotImplementedError(self.append_revision)
283
298
def set_revision_history(self, rev_history):
284
raise NotImplementedError('set_revision_history is abstract')
299
raise NotImplementedError(self.set_revision_history)
286
301
def revision_history(self):
287
302
"""Return sequence of revision hashes on to this branch."""
288
raise NotImplementedError('revision_history is abstract')
303
raise NotImplementedError(self.revision_history)
291
306
"""Return current revision number for this branch.
337
352
:param stop_revision: Updated until the given revision
340
raise NotImplementedError('update_revisions is abstract')
355
raise NotImplementedError(self.update_revisions)
342
357
def revision_id_to_revno(self, revision_id):
343
358
"""Given a revision id, return its revno"""
356
371
if history is None:
357
372
history = self.revision_history()
358
elif revno <= 0 or revno > len(history):
373
if revno <= 0 or revno > len(history):
359
374
raise bzrlib.errors.NoSuchRevision(self, revno)
360
375
return history[revno - 1]
362
377
def pull(self, source, overwrite=False, stop_revision=None):
363
raise NotImplementedError('pull is abstract')
378
raise NotImplementedError(self.pull)
365
380
def basis_tree(self):
366
"""Return `Tree` object for last revision.
368
If there are no revisions yet, return an `EmptyTree`.
381
"""Return `Tree` object for last revision."""
370
382
return self.repository.revision_tree(self.last_revision())
372
384
def rename_one(self, from_rel, to_rel):
422
434
def get_push_location(self):
423
435
"""Return the None or the location to push this branch to."""
424
raise NotImplementedError('get_push_location is abstract')
436
raise NotImplementedError(self.get_push_location)
426
438
def set_push_location(self, location):
427
439
"""Set a new push location for this branch."""
428
raise NotImplementedError('set_push_location is abstract')
440
raise NotImplementedError(self.set_push_location)
430
442
def set_parent(self, url):
431
raise NotImplementedError('set_parent is abstract')
443
raise NotImplementedError(self.set_parent)
433
445
@needs_write_lock
434
446
def update(self):
537
549
rev = self.repository.get_revision(revision_id)
538
550
new_history = rev.get_history(self.repository)[1:]
539
551
destination.set_revision_history(new_history)
540
parent = self.get_parent()
542
destination.set_parent(parent)
553
parent = self.get_parent()
554
except errors.InaccessibleParent, e:
555
mutter('parent was not accessible to copy: %s', e)
558
destination.set_parent(parent)
571
587
mainline_parent_id = revision_id
572
588
return BranchCheckResult(self)
590
def _get_checkout_format(self):
591
"""Return the most suitable metadir for a checkout of this branch.
592
Weaves are used if this branch's repostory uses weaves.
594
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
595
from bzrlib import repository
596
format = bzrdir.BzrDirMetaFormat1()
597
format.repository_format = repository.RepositoryFormat7()
599
format = self.repository.bzrdir.cloning_metadir()
602
def create_checkout(self, to_location, revision_id=None,
604
"""Create a checkout of a branch.
606
:param to_location: The url to produce the checkout at
607
:param revision_id: The revision to check out
608
:param lightweight: If True, produce a lightweight checkout, otherwise,
609
produce a bound branch (heavyweight checkout)
610
:return: The tree of the created checkout
612
t = transport.get_transport(to_location)
615
except errors.FileExists:
618
checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
619
BranchReferenceFormat().initialize(checkout, self)
621
format = self._get_checkout_format()
622
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
623
to_location, force_new_tree=False, format=format)
624
checkout = checkout_branch.bzrdir
625
checkout_branch.bind(self)
626
# pull up to the specified revision_id to set the initial
627
# branch tip correctly, and seed it with history.
628
checkout_branch.pull(self, stop_revision=revision_id)
629
return checkout.create_workingtree(revision_id)
575
632
class BranchFormat(object):
576
633
"""An encapsulation of the initialization and open routines for a format.
678
735
utf8_files = [('revision-history', ''),
679
736
('branch-name', ''),
681
control_files = LockableFiles(branch_transport, 'branch-lock',
738
control_files = lockable_files.LockableFiles(branch_transport,
739
'branch-lock', lockable_files.TransportLock)
683
740
control_files.create_lock()
684
741
control_files.lock_write()
739
796
utf8_files = [('revision-history', ''),
740
797
('branch-name', ''),
742
control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
799
control_files = lockable_files.LockableFiles(branch_transport, 'lock',
743
801
control_files.create_lock()
744
802
control_files.lock_write()
745
803
control_files.put_utf8('format', self.get_format_string())
764
822
format = BranchFormat.find_format(a_bzrdir)
765
823
assert format.__class__ == self.__class__
766
824
transport = a_bzrdir.get_branch_transport(None)
767
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
825
control_files = lockable_files.LockableFiles(transport, 'lock',
768
827
return BzrBranch5(_format=self,
769
828
_control_files=control_files,
770
829
a_bzrdir=a_bzrdir,
801
860
raise errors.UninitializableFormat(self)
802
861
mutter('creating branch reference in %s', a_bzrdir.transport.base)
803
862
branch_transport = a_bzrdir.get_branch_transport(self)
804
# FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
805
branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
806
branch_transport.put('format', StringIO(self.get_format_string()))
863
branch_transport.put_bytes('location',
864
target_branch.bzrdir.root_transport.base)
865
branch_transport.put_bytes('format', self.get_format_string())
807
866
return self.open(a_bzrdir, _found=True)
809
868
def __init__(self):
921
980
__repr__ = __str__
924
# TODO: It might be best to do this somewhere else,
925
# but it is nice for a Branch object to automatically
926
# cache it's information.
927
# Alternatively, we could have the Transport objects cache requests
928
# See the earlier discussion about how major objects (like Branch)
929
# should never expect their __del__ function to run.
930
# XXX: cache_root seems to be unused, 2006-01-13 mbp
931
if hasattr(self, 'cache_root') and self.cache_root is not None:
933
osutils.rmtree(self.cache_root)
936
self.cache_root = None
938
982
def _get_base(self):
939
983
return self._base
1063
1107
transaction = self.get_transaction()
1064
1108
history = transaction.map.find_revision_history()
1065
1109
if history is not None:
1066
mutter("cache hit for revision-history in %s", self)
1110
# mutter("cache hit for revision-history in %s", self)
1067
1111
return list(history)
1068
history = [l.rstrip('\r\n') for l in
1069
self.control_files.get_utf8('revision-history').readlines()]
1112
decode_utf8 = cache_utf8.decode
1113
history = [decode_utf8(l.rstrip('\r\n')) for l in
1114
self.control_files.get('revision-history').readlines()]
1070
1115
transaction.map.add_revision_history(history)
1071
1116
# this call is disabled because revision_history is
1072
1117
# not really an object yet, and the transaction is for objects.
1092
1137
# make a new revision history from the graph
1093
1138
current_rev_id = revision_id
1094
1139
new_history = []
1095
while current_rev_id not in (None, revision.NULL_REVISION):
1140
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1096
1141
new_history.append(current_rev_id)
1097
1142
current_rev_id_parents = stop_graph[current_rev_id]
1173
1218
# turn it into a url
1174
1219
if parent.startswith('/'):
1175
1220
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1176
return urlutils.join(self.base[:-1], parent)
1222
return urlutils.join(self.base[:-1], parent)
1223
except errors.InvalidURLJoin, e:
1224
raise errors.InaccessibleParent(parent, self.base)
1179
1227
def get_push_location(self):
1184
1232
def set_push_location(self, location):
1185
1233
"""See Branch.set_push_location."""
1186
self.get_config().set_user_option('push_location', location,
1234
self.get_config().set_user_option(
1235
'push_location', location,
1236
store=_mod_config.STORE_LOCATION_NORECURSE)
1189
1238
@needs_write_lock
1190
1239
def set_parent(self, url):
1206
1255
"use bzrlib.urlutils.escape")
1208
1257
url = urlutils.relative_url(self.base, url)
1209
self.control_files.put('parent', url + '\n')
1258
self.control_files.put('parent', StringIO(url + '\n'))
1211
1260
@deprecated_function(zero_nine)
1212
1261
def tree_config(self):
1287
1336
@needs_write_lock
1288
1337
def bind(self, other):
1289
"""Bind the local branch the other branch.
1338
"""Bind this branch to the branch other.
1340
This does not push or pull data between the branches, though it does
1341
check for divergence to raise an error when the branches are not
1342
either the same, or one a prefix of the other. That behaviour may not
1343
be useful, so that check may be removed in future.
1291
1345
:param other: The branch to bind to
1292
1346
:type other: Branch
1298
1352
# but binding itself may not be.
1299
1353
# Since we *have* to check at commit time, we don't
1300
1354
# *need* to check here
1303
# we are now equal to or a suffix of other.
1305
# Since we have 'pulled' from the remote location,
1306
# now we should try to pull in the opposite direction
1307
# in case the local tree has more revisions than the
1309
# There may be a different check you could do here
1310
# rather than actually trying to install revisions remotely.
1311
# TODO: capture an exception which indicates the remote branch
1313
# If it is up-to-date, this probably should not be a failure
1315
# lock other for write so the revision-history syncing cannot race
1319
# if this does not error, other now has the same last rev we do
1320
# it can only error if the pull from other was concurrent with
1321
# a commit to other from someone else.
1323
# until we ditch revision-history, we need to sync them up:
1324
self.set_revision_history(other.revision_history())
1325
# now other and self are up to date with each other and have the
1326
# same revision-history.
1356
# we want to raise diverged if:
1357
# last_rev is not in the other_last_rev history, AND
1358
# other_last_rev is not in our history, and do it without pulling
1360
last_rev = self.last_revision()
1361
if last_rev is not None:
1364
other_last_rev = other.last_revision()
1365
if other_last_rev is not None:
1366
# neither branch is new, we have to do some work to
1367
# ascertain diversion.
1368
remote_graph = other.repository.get_revision_graph(
1370
local_graph = self.repository.get_revision_graph(last_rev)
1371
if (last_rev not in remote_graph and
1372
other_last_rev not in local_graph):
1373
raise errors.DivergedBranches(self, other)
1330
1376
self.set_bound_location(other.base)
1332
1378
@needs_write_lock
1408
1454
@deprecated_function(zero_eight)
1409
1455
def is_control_file(*args, **kwargs):
1410
1456
"""See bzrlib.workingtree.is_control_file."""
1411
return bzrlib.workingtree.is_control_file(*args, **kwargs)
1457
from bzrlib import workingtree
1458
return workingtree.is_control_file(*args, **kwargs)