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
34
revision as _mod_revision,
40
from bzrlib.config import BranchConfig, TreeConfig
41
from bzrlib.lockable_files import LockableFiles, TransportLock
29
44
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,
45
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
46
HistoryMissing, InvalidRevisionId,
47
InvalidRevisionNumber, LockError, NoSuchFile,
34
48
NoSuchRevision, NoWorkingTree, NotVersionedError,
35
NotBranchError, UninitializableFormat,
36
UnlistableStore, UnlistableBranch,
49
NotBranchError, UninitializableFormat,
50
UnlistableStore, UnlistableBranch,
38
from bzrlib.lockable_files import LockableFiles, TransportLock
39
52
from bzrlib.symbol_versioning import (deprecated_function,
41
54
DEPRECATED_PARAMETER,
131
144
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
146
@deprecated_function(zero_eight)
133
147
def setup_caching(self, cache_root):
134
148
"""Subclasses that care about caching should override this, and set
135
149
up cached stores located under cache_root.
151
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
155
def get_config(self):
142
return bzrlib.config.BranchConfig(self)
156
return BranchConfig(self)
144
158
def _get_nick(self):
145
159
return self.get_config().get_nickname()
150
164
nick = property(_get_nick, _set_nick)
152
166
def is_locked(self):
153
raise NotImplementedError('is_locked is abstract')
167
raise NotImplementedError(self.is_locked)
155
169
def lock_write(self):
156
raise NotImplementedError('lock_write is abstract')
170
raise NotImplementedError(self.lock_write)
158
172
def lock_read(self):
159
raise NotImplementedError('lock_read is abstract')
173
raise NotImplementedError(self.lock_read)
161
175
def unlock(self):
162
raise NotImplementedError('unlock is abstract')
176
raise NotImplementedError(self.unlock)
164
178
def peek_lock_mode(self):
165
179
"""Return lock mode for the Branch: 'r', 'w' or None"""
166
180
raise NotImplementedError(self.peek_lock_mode)
168
182
def get_physical_lock_status(self):
169
raise NotImplementedError('get_physical_lock_status is abstract')
183
raise NotImplementedError(self.get_physical_lock_status)
171
185
def abspath(self, name):
172
186
"""Return absolute filename for something in the branch
213
227
last_revision = from_history[-1]
215
229
# no history in the source branch
216
last_revision = revision.NULL_REVISION
230
last_revision = _mod_revision.NULL_REVISION
217
231
return self.repository.fetch(from_branch.repository,
218
232
revision_id=last_revision,
272
286
def get_root_id(self):
273
287
"""Return the id of this branches root"""
274
raise NotImplementedError('get_root_id is abstract')
288
raise NotImplementedError(self.get_root_id)
276
290
def print_file(self, file, revision_id):
277
291
"""Print `file` to stdout."""
278
raise NotImplementedError('print_file is abstract')
292
raise NotImplementedError(self.print_file)
280
294
def append_revision(self, *revision_ids):
281
raise NotImplementedError('append_revision is abstract')
295
raise NotImplementedError(self.append_revision)
283
297
def set_revision_history(self, rev_history):
284
raise NotImplementedError('set_revision_history is abstract')
298
raise NotImplementedError(self.set_revision_history)
286
300
def revision_history(self):
287
301
"""Return sequence of revision hashes on to this branch."""
288
raise NotImplementedError('revision_history is abstract')
302
raise NotImplementedError(self.revision_history)
291
305
"""Return current revision number for this branch.
337
351
:param stop_revision: Updated until the given revision
340
raise NotImplementedError('update_revisions is abstract')
354
raise NotImplementedError(self.update_revisions)
342
356
def revision_id_to_revno(self, revision_id):
343
357
"""Given a revision id, return its revno"""
356
370
if history is None:
357
371
history = self.revision_history()
358
elif revno <= 0 or revno > len(history):
372
if revno <= 0 or revno > len(history):
359
373
raise bzrlib.errors.NoSuchRevision(self, revno)
360
374
return history[revno - 1]
362
376
def pull(self, source, overwrite=False, stop_revision=None):
363
raise NotImplementedError('pull is abstract')
377
raise NotImplementedError(self.pull)
365
379
def basis_tree(self):
366
"""Return `Tree` object for last revision.
368
If there are no revisions yet, return an `EmptyTree`.
380
"""Return `Tree` object for last revision."""
370
381
return self.repository.revision_tree(self.last_revision())
372
383
def rename_one(self, from_rel, to_rel):
422
433
def get_push_location(self):
423
434
"""Return the None or the location to push this branch to."""
424
raise NotImplementedError('get_push_location is abstract')
435
raise NotImplementedError(self.get_push_location)
426
437
def set_push_location(self, location):
427
438
"""Set a new push location for this branch."""
428
raise NotImplementedError('set_push_location is abstract')
439
raise NotImplementedError(self.set_push_location)
430
441
def set_parent(self, url):
431
raise NotImplementedError('set_parent is abstract')
442
raise NotImplementedError(self.set_parent)
433
444
@needs_write_lock
434
445
def update(self):
537
548
rev = self.repository.get_revision(revision_id)
538
549
new_history = rev.get_history(self.repository)[1:]
539
550
destination.set_revision_history(new_history)
540
parent = self.get_parent()
542
destination.set_parent(parent)
552
parent = self.get_parent()
553
except errors.InaccessibleParent, e:
554
mutter('parent was not accessible to copy: %s', e)
557
destination.set_parent(parent)
571
586
mainline_parent_id = revision_id
572
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()
601
def create_checkout(self, to_location, revision_id=None,
603
"""Create a checkout of a branch.
605
:param to_location: The url to produce the checkout at
606
:param revision_id: The revision to check out
607
:param lightweight: If True, produce a lightweight checkout, otherwise,
608
produce a bound branch (heavyweight checkout)
609
:return: The tree of the created checkout
611
t = transport.get_transport(to_location)
614
except errors.FileExists:
617
checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
618
BranchReferenceFormat().initialize(checkout, self)
620
format = self._get_checkout_format()
621
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
622
to_location, force_new_tree=False, format=format)
623
checkout = checkout_branch.bzrdir
624
checkout_branch.bind(self)
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)
628
return checkout.create_workingtree(revision_id)
575
631
class BranchFormat(object):
576
632
"""An encapsulation of the initialization and open routines for a format.
678
734
utf8_files = [('revision-history', ''),
679
735
('branch-name', ''),
681
control_files = LockableFiles(branch_transport, 'branch-lock',
737
control_files = lockable_files.LockableFiles(branch_transport,
738
'branch-lock', lockable_files.TransportLock)
683
739
control_files.create_lock()
684
740
control_files.lock_write()
739
795
utf8_files = [('revision-history', ''),
740
796
('branch-name', ''),
742
control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
798
control_files = lockable_files.LockableFiles(branch_transport, 'lock',
743
800
control_files.create_lock()
744
801
control_files.lock_write()
745
802
control_files.put_utf8('format', self.get_format_string())
764
821
format = BranchFormat.find_format(a_bzrdir)
765
822
assert format.__class__ == self.__class__
766
823
transport = a_bzrdir.get_branch_transport(None)
767
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
824
control_files = lockable_files.LockableFiles(transport, 'lock',
768
826
return BzrBranch5(_format=self,
769
827
_control_files=control_files,
770
828
a_bzrdir=a_bzrdir,
801
859
raise errors.UninitializableFormat(self)
802
860
mutter('creating branch reference in %s', a_bzrdir.transport.base)
803
861
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()))
862
branch_transport.put_bytes('location',
863
target_branch.bzrdir.root_transport.base)
864
branch_transport.put_bytes('format', self.get_format_string())
807
865
return self.open(a_bzrdir, _found=True)
809
867
def __init__(self):
921
979
__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
981
def _get_base(self):
939
982
return self._base
1063
1106
transaction = self.get_transaction()
1064
1107
history = transaction.map.find_revision_history()
1065
1108
if history is not None:
1066
mutter("cache hit for revision-history in %s", self)
1109
# mutter("cache hit for revision-history in %s", self)
1067
1110
return list(history)
1068
history = [l.rstrip('\r\n') for l in
1069
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()]
1070
1114
transaction.map.add_revision_history(history)
1071
1115
# this call is disabled because revision_history is
1072
1116
# not really an object yet, and the transaction is for objects.
1092
1136
# make a new revision history from the graph
1093
1137
current_rev_id = revision_id
1094
1138
new_history = []
1095
while current_rev_id not in (None, revision.NULL_REVISION):
1139
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1096
1140
new_history.append(current_rev_id)
1097
1141
current_rev_id_parents = stop_graph[current_rev_id]
1173
1217
# turn it into a url
1174
1218
if parent.startswith('/'):
1175
1219
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1176
return urlutils.join(self.base[:-1], parent)
1221
return urlutils.join(self.base[:-1], parent)
1222
except errors.InvalidURLJoin, e:
1223
raise errors.InaccessibleParent(parent, self.base)
1179
1226
def get_push_location(self):
1206
1253
"use bzrlib.urlutils.escape")
1208
1255
url = urlutils.relative_url(self.base, url)
1209
self.control_files.put('parent', url + '\n')
1256
self.control_files.put('parent', StringIO(url + '\n'))
1211
1258
@deprecated_function(zero_nine)
1212
1259
def tree_config(self):
1287
1334
@needs_write_lock
1288
1335
def bind(self, other):
1289
"""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.
1291
1343
:param other: The branch to bind to
1292
1344
:type other: Branch
1298
1350
# but binding itself may not be.
1299
1351
# Since we *have* to check at commit time, we don't
1300
1352
# *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.
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)
1330
1374
self.set_bound_location(other.base)
1332
1376
@needs_write_lock