21
21
from warnings import warn
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
28
36
from bzrlib.config import TreeConfig
29
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
30
38
import bzrlib.errors as errors
131
139
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
141
@deprecated_function(zero_eight)
133
142
def setup_caching(self, cache_root):
134
143
"""Subclasses that care about caching should override this, and set
135
144
up cached stores located under cache_root.
146
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
150
def get_config(self):
142
151
return bzrlib.config.BranchConfig(self)
150
159
nick = property(_get_nick, _set_nick)
152
161
def is_locked(self):
153
raise NotImplementedError('is_locked is abstract')
162
raise NotImplementedError(self.is_locked)
155
164
def lock_write(self):
156
raise NotImplementedError('lock_write is abstract')
165
raise NotImplementedError(self.lock_write)
158
167
def lock_read(self):
159
raise NotImplementedError('lock_read is abstract')
168
raise NotImplementedError(self.lock_read)
161
170
def unlock(self):
162
raise NotImplementedError('unlock is abstract')
171
raise NotImplementedError(self.unlock)
164
173
def peek_lock_mode(self):
165
174
"""Return lock mode for the Branch: 'r', 'w' or None"""
166
175
raise NotImplementedError(self.peek_lock_mode)
168
177
def get_physical_lock_status(self):
169
raise NotImplementedError('get_physical_lock_status is abstract')
178
raise NotImplementedError(self.get_physical_lock_status)
171
180
def abspath(self, name):
172
181
"""Return absolute filename for something in the branch
174
183
XXX: Robert Collins 20051017 what is this used for? why is it a branch
175
184
method and not a tree method.
177
raise NotImplementedError('abspath is abstract')
186
raise NotImplementedError(self.abspath)
179
188
def bind(self, other):
180
189
"""Bind the local branch the other branch.
272
281
def get_root_id(self):
273
282
"""Return the id of this branches root"""
274
raise NotImplementedError('get_root_id is abstract')
283
raise NotImplementedError(self.get_root_id)
276
285
def print_file(self, file, revision_id):
277
286
"""Print `file` to stdout."""
278
raise NotImplementedError('print_file is abstract')
287
raise NotImplementedError(self.print_file)
280
289
def append_revision(self, *revision_ids):
281
raise NotImplementedError('append_revision is abstract')
290
raise NotImplementedError(self.append_revision)
283
292
def set_revision_history(self, rev_history):
284
raise NotImplementedError('set_revision_history is abstract')
293
raise NotImplementedError(self.set_revision_history)
286
295
def revision_history(self):
287
296
"""Return sequence of revision hashes on to this branch."""
288
raise NotImplementedError('revision_history is abstract')
297
raise NotImplementedError(self.revision_history)
291
300
"""Return current revision number for this branch.
300
309
raise errors.UpgradeRequired(self.base)
302
311
def last_revision(self):
303
"""Return last patch hash, or None if no history."""
312
"""Return last revision id, or None"""
304
313
ph = self.revision_history()
337
346
:param stop_revision: Updated until the given revision
340
raise NotImplementedError('update_revisions is abstract')
349
raise NotImplementedError(self.update_revisions)
342
351
def revision_id_to_revno(self, revision_id):
343
352
"""Given a revision id, return its revno"""
356
365
if history is None:
357
366
history = self.revision_history()
358
elif revno <= 0 or revno > len(history):
367
if revno <= 0 or revno > len(history):
359
368
raise bzrlib.errors.NoSuchRevision(self, revno)
360
369
return history[revno - 1]
362
371
def pull(self, source, overwrite=False, stop_revision=None):
363
raise NotImplementedError('pull is abstract')
372
raise NotImplementedError(self.pull)
365
374
def basis_tree(self):
366
"""Return `Tree` object for last revision.
368
If there are no revisions yet, return an `EmptyTree`.
375
"""Return `Tree` object for last revision."""
370
376
return self.repository.revision_tree(self.last_revision())
372
378
def rename_one(self, from_rel, to_rel):
375
381
This can change the directory or the filename or both.
377
raise NotImplementedError('rename_one is abstract')
383
raise NotImplementedError(self.rename_one)
379
385
def move(self, from_paths, to_name):
390
396
This returns a list of (from_path, to_path) pairs for each
391
397
entry that is moved.
393
raise NotImplementedError('move is abstract')
399
raise NotImplementedError(self.move)
395
401
def get_parent(self):
396
402
"""Return the parent location of the branch.
399
405
pattern is that the user can override it by specifying a
402
raise NotImplementedError('get_parent is abstract')
408
raise NotImplementedError(self.get_parent)
404
410
def get_submit_branch(self):
405
411
"""Return the submit location of the branch.
422
428
def get_push_location(self):
423
429
"""Return the None or the location to push this branch to."""
424
raise NotImplementedError('get_push_location is abstract')
430
raise NotImplementedError(self.get_push_location)
426
432
def set_push_location(self, location):
427
433
"""Set a new push location for this branch."""
428
raise NotImplementedError('set_push_location is abstract')
434
raise NotImplementedError(self.set_push_location)
430
436
def set_parent(self, url):
431
raise NotImplementedError('set_parent is abstract')
437
raise NotImplementedError(self.set_parent)
433
439
@needs_write_lock
434
440
def update(self):
537
543
rev = self.repository.get_revision(revision_id)
538
544
new_history = rev.get_history(self.repository)[1:]
539
545
destination.set_revision_history(new_history)
540
parent = self.get_parent()
542
destination.set_parent(parent)
547
parent = self.get_parent()
548
except errors.InaccessibleParent, e:
549
mutter('parent was not accessible to copy: %s', e)
552
destination.set_parent(parent)
571
581
mainline_parent_id = revision_id
572
582
return BranchCheckResult(self)
584
def _get_checkout_format(self):
585
"""Return the most suitable metadir for a checkout of this branch.
586
Weaves are used if this branch's repostory uses weaves.
588
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
589
from bzrlib import repository
590
format = bzrdir.BzrDirMetaFormat1()
591
format.repository_format = repository.RepositoryFormat7()
593
format = self.repository.bzrdir.cloning_metadir()
596
def create_checkout(self, to_location, revision_id=None,
598
"""Create a checkout of a branch.
600
:param to_location: The url to produce the checkout at
601
:param revision_id: The revision to check out
602
:param lightweight: If True, produce a lightweight checkout, otherwise,
603
produce a bound branch (heavyweight checkout)
604
:return: The tree of the created checkout
606
t = transport.get_transport(to_location)
609
except errors.FileExists:
612
checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
613
BranchReferenceFormat().initialize(checkout, self)
615
format = self._get_checkout_format()
616
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
617
to_location, force_new_tree=False, format=format)
618
checkout = checkout_branch.bzrdir
619
checkout_branch.bind(self)
620
# pull up to the specified revision_id to set the initial
621
# branch tip correctly, and seed it with history.
622
checkout_branch.pull(self, stop_revision=revision_id)
623
return checkout.create_workingtree(revision_id)
575
626
class BranchFormat(object):
576
627
"""An encapsulation of the initialization and open routines for a format.
801
852
raise errors.UninitializableFormat(self)
802
853
mutter('creating branch reference in %s', a_bzrdir.transport.base)
803
854
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()))
855
branch_transport.put_bytes('location',
856
target_branch.bzrdir.root_transport.base)
857
branch_transport.put_bytes('format', self.get_format_string())
807
858
return self.open(a_bzrdir, _found=True)
809
860
def __init__(self):
921
972
__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
974
def _get_base(self):
939
975
return self._base
1063
1099
transaction = self.get_transaction()
1064
1100
history = transaction.map.find_revision_history()
1065
1101
if history is not None:
1066
mutter("cache hit for revision-history in %s", self)
1102
# mutter("cache hit for revision-history in %s", self)
1067
1103
return list(history)
1068
history = [l.rstrip('\r\n') for l in
1069
self.control_files.get_utf8('revision-history').readlines()]
1104
decode_utf8 = cache_utf8.decode
1105
history = [decode_utf8(l.rstrip('\r\n')) for l in
1106
self.control_files.get('revision-history').readlines()]
1070
1107
transaction.map.add_revision_history(history)
1071
1108
# this call is disabled because revision_history is
1072
1109
# not really an object yet, and the transaction is for objects.
1173
1210
# turn it into a url
1174
1211
if parent.startswith('/'):
1175
1212
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1176
return urlutils.join(self.base[:-1], parent)
1214
return urlutils.join(self.base[:-1], parent)
1215
except errors.InvalidURLJoin, e:
1216
raise errors.InaccessibleParent(parent, self.base)
1179
1219
def get_push_location(self):
1206
1246
"use bzrlib.urlutils.escape")
1208
1248
url = urlutils.relative_url(self.base, url)
1209
self.control_files.put('parent', url + '\n')
1249
self.control_files.put('parent', StringIO(url + '\n'))
1211
1251
@deprecated_function(zero_nine)
1212
1252
def tree_config(self):
1287
1327
@needs_write_lock
1288
1328
def bind(self, other):
1289
"""Bind the local branch the other branch.
1329
"""Bind this branch to the branch other.
1331
This does not push or pull data between the branches, though it does
1332
check for divergence to raise an error when the branches are not
1333
either the same, or one a prefix of the other. That behaviour may not
1334
be useful, so that check may be removed in future.
1291
1336
:param other: The branch to bind to
1292
1337
:type other: Branch
1298
1343
# but binding itself may not be.
1299
1344
# Since we *have* to check at commit time, we don't
1300
1345
# *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.
1347
# we want to raise diverged if:
1348
# last_rev is not in the other_last_rev history, AND
1349
# other_last_rev is not in our history, and do it without pulling
1351
last_rev = self.last_revision()
1352
if last_rev is not None:
1355
other_last_rev = other.last_revision()
1356
if other_last_rev is not None:
1357
# neither branch is new, we have to do some work to
1358
# ascertain diversion.
1359
remote_graph = other.repository.get_revision_graph(
1361
local_graph = self.repository.get_revision_graph(last_rev)
1362
if (last_rev not in remote_graph and
1363
other_last_rev not in local_graph):
1364
raise errors.DivergedBranches(self, other)
1330
1367
self.set_bound_location(other.base)
1332
1369
@needs_write_lock