15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from copy import deepcopy
18
19
from cStringIO import StringIO
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
24
from unittest import TestSuite
22
25
from warnings import warn
28
config as _mod_config,
33
revision as _mod_revision,
40
from bzrlib.config import BranchConfig, TreeConfig
28
import bzrlib.bzrdir as bzrdir
29
from bzrlib.config import TreeConfig
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
from bzrlib.delta import compare_trees
32
import bzrlib.errors as errors
33
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
NoSuchRevision, HistoryMissing, NotBranchError,
35
DivergedBranches, LockError,
36
UninitializableFormat,
38
UnlistableBranch, NoSuchFile, NotVersionedError,
40
import bzrlib.inventory as inventory
41
from bzrlib.inventory import Inventory
41
42
from bzrlib.lockable_files import LockableFiles, TransportLock
42
from bzrlib.tag import (
48
from bzrlib.decorators import needs_read_lock, needs_write_lock
49
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
50
HistoryMissing, InvalidRevisionId,
51
InvalidRevisionNumber, LockError, NoSuchFile,
52
NoSuchRevision, NoWorkingTree, NotVersionedError,
53
NotBranchError, UninitializableFormat,
54
UnlistableStore, UnlistableBranch,
56
from bzrlib.hooks import Hooks
57
from bzrlib.symbol_versioning import (deprecated_function,
61
zero_eight, zero_nine, zero_sixteen,
43
from bzrlib.lockdir import LockDir
44
from bzrlib.osutils import (isdir, quotefn,
45
rename, splitpath, sha_file,
46
file_kind, abspath, normpath, pathjoin,
50
from bzrlib.textui import show_status
63
51
from bzrlib.trace import mutter, note
52
from bzrlib.tree import EmptyTree, RevisionTree
53
from bzrlib.repository import Repository
54
from bzrlib.revision import (
55
get_intervening_revisions,
60
from bzrlib.store import copy_all
61
from bzrlib.symbol_versioning import *
62
import bzrlib.transactions as transactions
63
from bzrlib.transport import Transport, get_transport
64
from bzrlib.tree import EmptyTree, RevisionTree
66
69
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
67
70
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
68
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
71
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
71
74
# TODO: Maybe include checks for common corruption of newlines, etc?
157
152
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
159
@deprecated_function(zero_eight)
160
154
def setup_caching(self, cache_root):
161
155
"""Subclasses that care about caching should override this, and set
162
156
up cached stores located under cache_root.
164
NOTE: This is unused.
168
def get_config(self):
169
return BranchConfig(self)
158
# seems to be unused, 2006-01-13 mbp
159
warn('%s is deprecated' % self.setup_caching)
160
self.cache_root = cache_root
171
162
def _get_nick(self):
172
return self.get_config().get_nickname()
163
cfg = self.tree_config()
164
return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
174
166
def _set_nick(self, nick):
175
self.get_config().set_user_option('nickname', nick, warn_masked=True)
167
cfg = self.tree_config()
168
cfg.set_option(nick, "nickname")
169
assert cfg.get_option("nickname") == nick
177
171
nick = property(_get_nick, _set_nick)
179
173
def is_locked(self):
180
raise NotImplementedError(self.is_locked)
174
raise NotImplementedError('is_locked is abstract')
182
176
def lock_write(self):
183
raise NotImplementedError(self.lock_write)
177
raise NotImplementedError('lock_write is abstract')
185
179
def lock_read(self):
186
raise NotImplementedError(self.lock_read)
180
raise NotImplementedError('lock_read is abstract')
188
182
def unlock(self):
189
raise NotImplementedError(self.unlock)
183
raise NotImplementedError('unlock is abstract')
191
185
def peek_lock_mode(self):
192
186
"""Return lock mode for the Branch: 'r', 'w' or None"""
193
187
raise NotImplementedError(self.peek_lock_mode)
195
189
def get_physical_lock_status(self):
196
raise NotImplementedError(self.get_physical_lock_status)
199
def get_revision_id_to_revno_map(self):
200
"""Return the revision_id => dotted revno map.
202
This will be regenerated on demand, but will be cached.
204
:return: A dictionary mapping revision_id => dotted revno.
205
This dictionary should not be modified by the caller.
207
if self._revision_id_to_revno_cache is not None:
208
mapping = self._revision_id_to_revno_cache
210
mapping = self._gen_revno_map()
211
self._cache_revision_id_to_revno(mapping)
212
# TODO: jam 20070417 Since this is being cached, should we be returning
214
# I would rather not, and instead just declare that users should not
215
# modify the return value.
218
def _gen_revno_map(self):
219
"""Create a new mapping from revision ids to dotted revnos.
221
Dotted revnos are generated based on the current tip in the revision
223
This is the worker function for get_revision_id_to_revno_map, which
224
just caches the return value.
226
:return: A dictionary mapping revision_id => dotted revno.
228
last_revision = self.last_revision()
229
revision_graph = self.repository.get_revision_graph(last_revision)
230
merge_sorted_revisions = tsort.merge_sort(
235
revision_id_to_revno = dict((rev_id, revno)
236
for seq_num, rev_id, depth, revno, end_of_merge
237
in merge_sorted_revisions)
238
return revision_id_to_revno
240
def leave_lock_in_place(self):
241
"""Tell this branch object not to release the physical lock when this
244
If lock_write doesn't return a token, then this method is not supported.
246
self.control_files.leave_in_place()
248
def dont_leave_lock_in_place(self):
249
"""Tell this branch object to release the physical lock when this
250
object is unlocked, even if it didn't originally acquire it.
252
If lock_write doesn't return a token, then this method is not supported.
254
self.control_files.dont_leave_in_place()
190
raise NotImplementedError('get_physical_lock_status is abstract')
256
192
def abspath(self, name):
257
193
"""Return absolute filename for something in the branch
347
def get_revision_delta(self, revno):
348
"""Return the delta for one revision.
350
The delta is relative to its mainline predecessor, or the
351
empty tree for revision 1.
353
assert isinstance(revno, int)
354
rh = self.revision_history()
355
if not (1 <= revno <= len(rh)):
356
raise InvalidRevisionNumber(revno)
357
return self.repository.get_revision_delta(rh[revno-1])
359
@deprecated_method(zero_sixteen)
360
261
def get_root_id(self):
361
"""Return the id of this branches root
363
Deprecated: branches don't have root ids-- trees do.
364
Use basis_tree().get_root_id() instead.
366
raise NotImplementedError(self.get_root_id)
262
"""Return the id of this branches root"""
263
raise NotImplementedError('get_root_id is abstract')
368
265
def print_file(self, file, revision_id):
369
266
"""Print `file` to stdout."""
370
raise NotImplementedError(self.print_file)
267
raise NotImplementedError('print_file is abstract')
372
269
def append_revision(self, *revision_ids):
373
raise NotImplementedError(self.append_revision)
270
raise NotImplementedError('append_revision is abstract')
375
272
def set_revision_history(self, rev_history):
376
raise NotImplementedError(self.set_revision_history)
378
def _cache_revision_history(self, rev_history):
379
"""Set the cached revision history to rev_history.
381
The revision_history method will use this cache to avoid regenerating
382
the revision history.
384
This API is semi-public; it only for use by subclasses, all other code
385
should consider it to be private.
387
self._revision_history_cache = rev_history
389
def _cache_revision_id_to_revno(self, revision_id_to_revno):
390
"""Set the cached revision_id => revno map to revision_id_to_revno.
392
This API is semi-public; it only for use by subclasses, all other code
393
should consider it to be private.
395
self._revision_id_to_revno_cache = revision_id_to_revno
397
def _clear_cached_state(self):
398
"""Clear any cached data on this branch, e.g. cached revision history.
400
This means the next call to revision_history will need to call
401
_gen_revision_history.
403
This API is semi-public; it only for use by subclasses, all other code
404
should consider it to be private.
406
self._revision_history_cache = None
407
self._revision_id_to_revno_cache = None
409
def _gen_revision_history(self):
410
"""Return sequence of revision hashes on to this branch.
412
Unlike revision_history, this method always regenerates or rereads the
413
revision history, i.e. it does not cache the result, so repeated calls
416
Concrete subclasses should override this instead of revision_history so
417
that subclasses do not need to deal with caching logic.
419
This API is semi-public; it only for use by subclasses, all other code
420
should consider it to be private.
422
raise NotImplementedError(self._gen_revision_history)
273
raise NotImplementedError('set_revision_history is abstract')
425
275
def revision_history(self):
426
"""Return sequence of revision hashes on to this branch.
428
This method will cache the revision history for as long as it is safe to
431
if self._revision_history_cache is not None:
432
history = self._revision_history_cache
434
history = self._gen_revision_history()
435
self._cache_revision_history(history)
276
"""Return sequence of revision hashes on to this branch."""
277
raise NotImplementedError('revision_history is abstract')
439
280
"""Return current revision number for this branch.
688
519
result.set_parent(self.bzrdir.root_transport.base)
691
def _synchronize_history(self, destination, revision_id):
692
"""Synchronize last revision and revision history between branches.
694
This version is most efficient when the destination is also a
695
BzrBranch5, but works for BzrBranch6 as long as the revision
696
history is the true lefthand parent history, and all of the revisions
697
are in the destination's repository. If not, set_revision_history
700
:param destination: The branch to copy the history into
701
:param revision_id: The revision-id to truncate history at. May
702
be None to copy complete history.
523
def copy_content_into(self, destination, revision_id=None):
524
"""Copy the content of self into destination.
526
revision_id: if not None, the revision history in the new branch will
527
be truncated to end with revision_id.
704
if revision_id == _mod_revision.NULL_REVISION:
706
529
new_history = self.revision_history()
707
if revision_id is not None and new_history != []:
708
revision_id = osutils.safe_revision_id(revision_id)
530
if revision_id is not None:
710
532
new_history = new_history[:new_history.index(revision_id) + 1]
711
533
except ValueError:
712
534
rev = self.repository.get_revision(revision_id)
713
535
new_history = rev.get_history(self.repository)[1:]
714
536
destination.set_revision_history(new_history)
717
def copy_content_into(self, destination, revision_id=None):
718
"""Copy the content of self into destination.
720
revision_id: if not None, the revision history in the new branch will
721
be truncated to end with revision_id.
723
self._synchronize_history(destination, revision_id)
725
parent = self.get_parent()
726
except errors.InaccessibleParent, e:
727
mutter('parent was not accessible to copy: %s', e)
730
destination.set_parent(parent)
731
self.tags.merge_to(destination.tags)
735
"""Check consistency of the branch.
737
In particular this checks that revisions given in the revision-history
738
do actually match up in the revision graph, and that they're all
739
present in the repository.
741
Callers will typically also want to check the repository.
743
:return: A BranchCheckResult.
745
mainline_parent_id = None
746
for revision_id in self.revision_history():
748
revision = self.repository.get_revision(revision_id)
749
except errors.NoSuchRevision, e:
750
raise errors.BzrCheckError("mainline revision {%s} not in repository"
752
# In general the first entry on the revision history has no parents.
753
# But it's not illegal for it to have parents listed; this can happen
754
# in imports from Arch when the parents weren't reachable.
755
if mainline_parent_id is not None:
756
if mainline_parent_id not in revision.parent_ids:
757
raise errors.BzrCheckError("previous revision {%s} not listed among "
759
% (mainline_parent_id, revision_id))
760
mainline_parent_id = revision_id
761
return BranchCheckResult(self)
763
def _get_checkout_format(self):
764
"""Return the most suitable metadir for a checkout of this branch.
765
Weaves are used if this branch's repository uses weaves.
767
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
768
from bzrlib.repofmt import weaverepo
769
format = bzrdir.BzrDirMetaFormat1()
770
format.repository_format = weaverepo.RepositoryFormat7()
772
format = self.repository.bzrdir.checkout_metadir()
773
format.set_branch_format(self._format)
776
def create_checkout(self, to_location, revision_id=None,
778
"""Create a checkout of a branch.
780
:param to_location: The url to produce the checkout at
781
:param revision_id: The revision to check out
782
:param lightweight: If True, produce a lightweight checkout, otherwise,
783
produce a bound branch (heavyweight checkout)
784
:return: The tree of the created checkout
786
t = transport.get_transport(to_location)
789
format = self._get_checkout_format()
790
checkout = format.initialize_on_transport(t)
791
BranchReferenceFormat().initialize(checkout, self)
793
format = self._get_checkout_format()
794
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
795
to_location, force_new_tree=False, format=format)
796
checkout = checkout_branch.bzrdir
797
checkout_branch.bind(self)
798
# pull up to the specified revision_id to set the initial
799
# branch tip correctly, and seed it with history.
800
checkout_branch.pull(self, stop_revision=revision_id)
801
tree = checkout.create_workingtree(revision_id)
802
basis_tree = tree.basis_tree()
803
basis_tree.lock_read()
805
for path, file_id in basis_tree.iter_references():
806
reference_parent = self.reference_parent(file_id, path)
807
reference_parent.create_checkout(tree.abspath(path),
808
basis_tree.get_reference_revision(file_id, path),
814
def reference_parent(self, file_id, path):
815
"""Return the parent branch for a tree-reference file_id
816
:param file_id: The file_id of the tree reference
817
:param path: The path of the file_id in the tree
818
:return: A branch associated with the file_id
820
# FIXME should provide multiple branches, based on config
821
return Branch.open(self.bzrdir.root_transport.clone(path).base)
823
def supports_tags(self):
824
return self._format.supports_tags()
537
parent = self.get_parent()
539
destination.set_parent(parent)
827
542
class BranchFormat(object):
960
625
def __str__(self):
961
626
return self.get_format_string().rstrip()
963
def supports_tags(self):
964
"""True if this format supports tags stored in the branch"""
965
return False # by default
967
# XXX: Probably doesn't really belong here -- mbp 20070212
968
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
970
branch_transport = a_bzrdir.get_branch_transport(self)
971
control_files = lockable_files.LockableFiles(branch_transport,
972
lock_filename, lock_class)
973
control_files.create_lock()
974
control_files.lock_write()
976
for filename, content in utf8_files:
977
control_files.put_utf8(filename, content)
979
control_files.unlock()
982
class BranchHooks(Hooks):
983
"""A dictionary mapping hook name to a list of callables for branch hooks.
985
e.g. ['set_rh'] Is the list of items to be called when the
986
set_revision_history function is invoked.
990
"""Create the default hooks.
992
These are all empty initially, because by default nothing should get
996
# Introduced in 0.15:
997
# invoked whenever the revision history has been set
998
# with set_revision_history. The api signature is
999
# (branch, revision_history), and the branch will
1002
# invoked after a push operation completes.
1003
# the api signature is
1005
# containing the members
1006
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1007
# where local is the local target branch or None, master is the target
1008
# master branch, and the rest should be self explanatory. The source
1009
# is read locked and the target branches write locked. Source will
1010
# be the local low-latency branch.
1011
self['post_push'] = []
1012
# invoked after a pull operation completes.
1013
# the api signature is
1015
# containing the members
1016
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1017
# where local is the local branch or None, master is the target
1018
# master branch, and the rest should be self explanatory. The source
1019
# is read locked and the target branches write locked. The local
1020
# branch is the low-latency branch.
1021
self['post_pull'] = []
1022
# invoked after a commit operation completes.
1023
# the api signature is
1024
# (local, master, old_revno, old_revid, new_revno, new_revid)
1025
# old_revid is NULL_REVISION for the first commit to a branch.
1026
self['post_commit'] = []
1027
# invoked after a uncommit operation completes.
1028
# the api signature is
1029
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1030
# local is the local branch or None, master is the target branch,
1031
# and an empty branch recieves new_revno of 0, new_revid of None.
1032
self['post_uncommit'] = []
1035
# install the default hooks into the Branch class.
1036
Branch.hooks = BranchHooks()
1039
629
class BzrBranchFormat4(BranchFormat):
1040
630
"""Bzr branch format 4.
1273
828
it's writable, and can be accessed via the normal filesystem API.
1276
def __init__(self, _format=None,
831
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
832
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1277
833
_control_files=None, a_bzrdir=None, _repository=None):
1278
"""Create new branch object at a particular location."""
1279
Branch.__init__(self)
834
"""Create new branch object at a particular location.
836
transport -- A Transport object, defining how to access files.
838
init -- If True, create new control files in a previously
839
unversioned directory. If False, the branch must already
842
relax_version_check -- If true, the usual check for the branch
843
version is not applied. This is intended only for
844
upgrade/recovery type use; it's not guaranteed that
845
all operations will work on old format branches.
1280
847
if a_bzrdir is None:
1281
raise ValueError('a_bzrdir must be supplied')
848
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1283
850
self.bzrdir = a_bzrdir
1284
# self._transport used to point to the directory containing the
1285
# control directory, but was not used - now it's just the transport
1286
# for the branch control files. mbp 20070212
1287
self._base = self.bzrdir.transport.clone('..').base
851
self._transport = self.bzrdir.transport.clone('..')
852
self._base = self._transport.base
1288
853
self._format = _format
1289
854
if _control_files is None:
1290
raise ValueError('BzrBranch _control_files is None')
855
raise BzrBadParameterMissing('_control_files')
1291
856
self.control_files = _control_files
1292
self._transport = _control_files._transport
857
if deprecated_passed(init):
858
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
859
"deprecated as of bzr 0.8. Please use Branch.create().",
863
# this is slower than before deprecation, oh well never mind.
865
self._initialize(transport.base)
866
self._check_format(_format)
867
if deprecated_passed(relax_version_check):
868
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
869
"relax_version_check parameter is deprecated as of bzr 0.8. "
870
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
874
if (not relax_version_check
875
and not self._format.is_supported()):
876
raise errors.UnsupportedFormatError(
877
'sorry, branch format %r not supported' % fmt,
878
['use a different bzr version',
879
'or remove the .bzr directory'
880
' and "bzr init" again'])
881
if deprecated_passed(transport):
882
warn("BzrBranch.__init__(transport=XXX...): The transport "
883
"parameter is deprecated as of bzr 0.8. "
884
"Please use Branch.open, or bzrdir.open_branch().",
1293
887
self.repository = _repository
1295
889
def __str__(self):
1362
997
@needs_write_lock
1363
998
def append_revision(self, *revision_ids):
1364
999
"""See Branch.append_revision."""
1365
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1366
1000
for revision_id in revision_ids:
1367
_mod_revision.check_not_reserved_id(revision_id)
1368
1001
mutter("add {%s} to revision-history" % revision_id)
1369
1002
rev_history = self.revision_history()
1370
1003
rev_history.extend(revision_ids)
1371
1004
self.set_revision_history(rev_history)
1373
def _write_revision_history(self, history):
1374
"""Factored out of set_revision_history.
1376
This performs the actual writing to disk.
1377
It is intended to be called by BzrBranch5.set_revision_history."""
1378
self.control_files.put_bytes(
1379
'revision-history', '\n'.join(history))
1381
1006
@needs_write_lock
1382
1007
def set_revision_history(self, rev_history):
1383
1008
"""See Branch.set_revision_history."""
1384
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1385
self._clear_cached_state()
1386
self._write_revision_history(rev_history)
1387
self._cache_revision_history(rev_history)
1388
for hook in Branch.hooks['set_rh']:
1389
hook(self, rev_history)
1392
def set_last_revision_info(self, revno, revision_id):
1393
revision_id = osutils.safe_revision_id(revision_id)
1394
history = self._lefthand_history(revision_id)
1395
assert len(history) == revno, '%d != %d' % (len(history), revno)
1396
self.set_revision_history(history)
1398
def _gen_revision_history(self):
1399
history = self.control_files.get('revision-history').read().split('\n')
1400
if history[-1:] == ['']:
1401
# There shouldn't be a trailing newline, but just in case.
1405
def _lefthand_history(self, revision_id, last_rev=None,
1407
# stop_revision must be a descendant of last_revision
1408
stop_graph = self.repository.get_revision_graph(revision_id)
1409
if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1410
and last_rev not in stop_graph):
1411
# our previous tip is not merged into stop_revision
1412
raise errors.DivergedBranches(self, other_branch)
1413
# make a new revision history from the graph
1414
current_rev_id = revision_id
1416
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1417
new_history.append(current_rev_id)
1418
current_rev_id_parents = stop_graph[current_rev_id]
1420
current_rev_id = current_rev_id_parents[0]
1422
current_rev_id = None
1423
new_history.reverse()
1427
def generate_revision_history(self, revision_id, last_rev=None,
1429
"""Create a new revision history that will finish with revision_id.
1431
:param revision_id: the new tip to use.
1432
:param last_rev: The previous last_revision. If not None, then this
1433
must be a ancestory of revision_id, or DivergedBranches is raised.
1434
:param other_branch: The other branch that DivergedBranches should
1435
raise with respect to.
1009
self.control_files.put_utf8(
1010
'revision-history', '\n'.join(rev_history))
1011
transaction = self.get_transaction()
1012
history = transaction.map.find_revision_history()
1013
if history is not None:
1014
# update the revision history in the identity map.
1015
history[:] = list(rev_history)
1016
# this call is disabled because revision_history is
1017
# not really an object yet, and the transaction is for objects.
1018
# transaction.register_dirty(history)
1020
transaction.map.add_revision_history(rev_history)
1021
# this call is disabled because revision_history is
1022
# not really an object yet, and the transaction is for objects.
1023
# transaction.register_clean(history)
1025
def get_revision_delta(self, revno):
1026
"""Return the delta for one revision.
1028
The delta is relative to its mainline predecessor, or the
1029
empty tree for revision 1.
1437
revision_id = osutils.safe_revision_id(revision_id)
1438
self.set_revision_history(self._lefthand_history(revision_id,
1439
last_rev, other_branch))
1031
assert isinstance(revno, int)
1032
rh = self.revision_history()
1033
if not (1 <= revno <= len(rh)):
1034
raise InvalidRevisionNumber(revno)
1036
# revno is 1-based; list is 0-based
1038
new_tree = self.repository.revision_tree(rh[revno-1])
1040
old_tree = EmptyTree()
1042
old_tree = self.repository.revision_tree(rh[revno-2])
1043
return compare_trees(old_tree, new_tree)
1046
def revision_history(self):
1047
"""See Branch.revision_history."""
1048
transaction = self.get_transaction()
1049
history = transaction.map.find_revision_history()
1050
if history is not None:
1051
mutter("cache hit for revision-history in %s", self)
1052
return list(history)
1053
history = [l.rstrip('\r\n') for l in
1054
self.control_files.get_utf8('revision-history').readlines()]
1055
transaction.map.add_revision_history(history)
1056
# this call is disabled because revision_history is
1057
# not really an object yet, and the transaction is for objects.
1058
# transaction.register_clean(history, precious=True)
1059
return list(history)
1441
1061
@needs_write_lock
1442
1062
def update_revisions(self, other, stop_revision=None):
1481
1132
return self.bzrdir.open_workingtree()
1483
1134
@needs_write_lock
1484
def pull(self, source, overwrite=False, stop_revision=None,
1485
_hook_master=None, run_hooks=True):
1488
:param _hook_master: Private parameter - set the branch to
1489
be supplied as the master to push hooks.
1490
:param run_hooks: Private parameter - if false, this branch
1491
is being called because it's the master of the primary branch,
1492
so it should not run its hooks.
1494
result = PullResult()
1495
result.source_branch = source
1496
result.target_branch = self
1135
def pull(self, source, overwrite=False, stop_revision=None):
1136
"""See Branch.pull."""
1497
1137
source.lock_read()
1499
result.old_revno, result.old_revid = self.last_revision_info()
1139
old_count = len(self.revision_history())
1501
self.update_revisions(source, stop_revision)
1141
self.update_revisions(source,stop_revision)
1502
1142
except DivergedBranches:
1503
1143
if not overwrite:
1506
if stop_revision is None:
1507
stop_revision = source.last_revision()
1508
self.generate_revision_history(stop_revision)
1509
result.tag_conflicts = source.tags.merge_to(self.tags)
1510
result.new_revno, result.new_revid = self.last_revision_info()
1512
result.master_branch = _hook_master
1513
result.local_branch = self
1515
result.master_branch = self
1516
result.local_branch = None
1518
for hook in Branch.hooks['post_pull']:
1146
self.set_revision_history(source.revision_history())
1147
new_count = len(self.revision_history())
1148
return new_count - old_count
1521
1150
source.unlock()
1524
def _get_parent_location(self):
1152
def get_parent(self):
1153
"""See Branch.get_parent."""
1525
1155
_locs = ['parent', 'pull', 'x-pull']
1526
1156
for l in _locs:
1528
return self.control_files.get(l).read().strip('\n')
1158
return self.control_files.get_utf8(l).read().strip('\n')
1529
1159
except NoSuchFile:
1534
def push(self, target, overwrite=False, stop_revision=None,
1535
_override_hook_source_branch=None):
1538
This is the basic concrete implementation of push()
1540
:param _override_hook_source_branch: If specified, run
1541
the hooks passing this Branch as the source, rather than self.
1542
This is for use of RemoteBranch, where push is delegated to the
1543
underlying vfs-based Branch.
1545
# TODO: Public option to disable running hooks - should be trivial but
1549
result = self._push_with_bound_branches(target, overwrite,
1551
_override_hook_source_branch=_override_hook_source_branch)
1556
def _push_with_bound_branches(self, target, overwrite,
1558
_override_hook_source_branch=None):
1559
"""Push from self into target, and into target's master if any.
1561
This is on the base BzrBranch class even though it doesn't support
1562
bound branches because the *target* might be bound.
1565
if _override_hook_source_branch:
1566
result.source_branch = _override_hook_source_branch
1567
for hook in Branch.hooks['post_push']:
1570
bound_location = target.get_bound_location()
1571
if bound_location and target.base != bound_location:
1572
# there is a master branch.
1574
# XXX: Why the second check? Is it even supported for a branch to
1575
# be bound to itself? -- mbp 20070507
1576
master_branch = target.get_master_branch()
1577
master_branch.lock_write()
1579
# push into the master from this branch.
1580
self._basic_push(master_branch, overwrite, stop_revision)
1581
# and push into the target branch from this. Note that we push from
1582
# this branch again, because its considered the highest bandwidth
1584
result = self._basic_push(target, overwrite, stop_revision)
1585
result.master_branch = master_branch
1586
result.local_branch = target
1590
master_branch.unlock()
1593
result = self._basic_push(target, overwrite, stop_revision)
1594
# TODO: Why set master_branch and local_branch if there's no
1595
# binding? Maybe cleaner to just leave them unset? -- mbp
1597
result.master_branch = target
1598
result.local_branch = None
1602
def _basic_push(self, target, overwrite, stop_revision):
1603
"""Basic implementation of push without bound branches or hooks.
1605
Must be called with self read locked and target write locked.
1607
result = PushResult()
1608
result.source_branch = self
1609
result.target_branch = target
1610
result.old_revno, result.old_revid = target.last_revision_info()
1612
target.update_revisions(self, stop_revision)
1613
except DivergedBranches:
1617
target.set_revision_history(self.revision_history())
1618
result.tag_conflicts = self.tags.merge_to(target.tags)
1619
result.new_revno, result.new_revid = target.last_revision_info()
1622
def get_parent(self):
1623
"""See Branch.get_parent."""
1625
assert self.base[-1] == '/'
1626
parent = self._get_parent_location()
1629
# This is an old-format absolute path to a local branch
1630
# turn it into a url
1631
if parent.startswith('/'):
1632
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1634
return urlutils.join(self.base[:-1], parent)
1635
except errors.InvalidURLJoin, e:
1636
raise errors.InaccessibleParent(parent, self.base)
1163
def get_push_location(self):
1164
"""See Branch.get_push_location."""
1165
config = bzrlib.config.BranchConfig(self)
1166
push_loc = config.get_user_option('push_location')
1638
1169
def set_push_location(self, location):
1639
1170
"""See Branch.set_push_location."""
1640
self.get_config().set_user_option(
1641
'push_location', location,
1642
store=_mod_config.STORE_LOCATION_NORECURSE)
1171
config = bzrlib.config.LocationConfig(self.base)
1172
config.set_user_option('push_location', location)
1644
1174
@needs_write_lock
1645
1175
def set_parent(self, url):
1816
1318
if master is not None:
1817
1319
old_tip = self.last_revision()
1818
1320
self.pull(master, overwrite=True)
1819
if old_tip in self.repository.get_ancestry(
1820
_mod_revision.ensure_null(self.last_revision()),
1321
if old_tip in self.repository.get_ancestry(self.last_revision()):
1827
class BzrBranchExperimental(BzrBranch5):
1828
"""Bzr experimental branch format
1831
- a revision-history file.
1833
- a lock dir guarding the branch itself
1834
- all of this stored in a branch/ subdirectory
1835
- works with shared repositories.
1836
- a tag dictionary in the branch
1838
This format is new in bzr 0.15, but shouldn't be used for real data,
1841
This class acts as it's own BranchFormat.
1327
class BranchTestProviderAdapter(object):
1328
"""A tool to generate a suite testing multiple branch formats at once.
1330
This is done by copying the test once for each transport and injecting
1331
the transport_server, transport_readonly_server, and branch_format
1332
classes into each copy. Each copy is also given a new id() to make it
1844
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1847
def get_format_string(cls):
1848
"""See BranchFormat.get_format_string()."""
1849
return "Bazaar-NG branch format experimental\n"
1852
def get_format_description(cls):
1853
"""See BranchFormat.get_format_description()."""
1854
return "Experimental branch format"
1857
def get_reference(cls, a_bzrdir):
1858
"""Get the target reference of the branch in a_bzrdir.
1860
format probing must have been completed before calling
1861
this method - it is assumed that the format of the branch
1862
in a_bzrdir is correct.
1864
:param a_bzrdir: The bzrdir to get the branch data from.
1865
:return: None if the branch is not a reference branch.
1870
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1872
branch_transport = a_bzrdir.get_branch_transport(cls)
1873
control_files = lockable_files.LockableFiles(branch_transport,
1874
lock_filename, lock_class)
1875
control_files.create_lock()
1876
control_files.lock_write()
1878
for filename, content in utf8_files:
1879
control_files.put_utf8(filename, content)
1881
control_files.unlock()
1884
def initialize(cls, a_bzrdir):
1885
"""Create a branch of this format in a_bzrdir."""
1886
utf8_files = [('format', cls.get_format_string()),
1887
('revision-history', ''),
1888
('branch-name', ''),
1891
cls._initialize_control_files(a_bzrdir, utf8_files,
1892
'lock', lockdir.LockDir)
1893
return cls.open(a_bzrdir, _found=True)
1896
def open(cls, a_bzrdir, _found=False):
1897
"""Return the branch object for a_bzrdir
1899
_found is a private parameter, do not use it. It is used to indicate
1900
if format probing has already be done.
1903
format = BranchFormat.find_format(a_bzrdir)
1904
assert format.__class__ == cls
1905
transport = a_bzrdir.get_branch_transport(None)
1906
control_files = lockable_files.LockableFiles(transport, 'lock',
1908
return cls(_format=cls,
1909
_control_files=control_files,
1911
_repository=a_bzrdir.find_repository())
1914
def is_supported(cls):
1917
def _make_tags(self):
1918
return BasicTags(self)
1921
def supports_tags(cls):
1925
BranchFormat.register_format(BzrBranchExperimental)
1928
class BzrBranch6(BzrBranch5):
1931
def last_revision_info(self):
1932
revision_string = self.control_files.get('last-revision').read()
1933
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1934
revision_id = cache_utf8.get_cached_utf8(revision_id)
1936
return revno, revision_id
1938
def last_revision(self):
1939
"""Return last revision id, or None"""
1940
revision_id = self.last_revision_info()[1]
1941
if revision_id == _mod_revision.NULL_REVISION:
1945
def _write_last_revision_info(self, revno, revision_id):
1946
"""Simply write out the revision id, with no checks.
1948
Use set_last_revision_info to perform this safely.
1950
Does not update the revision_history cache.
1951
Intended to be called by set_last_revision_info and
1952
_write_revision_history.
1954
if revision_id is None:
1955
revision_id = 'null:'
1956
out_string = '%d %s\n' % (revno, revision_id)
1957
self.control_files.put_bytes('last-revision', out_string)
1960
def set_last_revision_info(self, revno, revision_id):
1961
revision_id = osutils.safe_revision_id(revision_id)
1962
if self._get_append_revisions_only():
1963
self._check_history_violation(revision_id)
1964
self._write_last_revision_info(revno, revision_id)
1965
self._clear_cached_state()
1967
def _check_history_violation(self, revision_id):
1968
last_revision = _mod_revision.ensure_null(self.last_revision())
1969
if _mod_revision.is_null(last_revision):
1971
if last_revision not in self._lefthand_history(revision_id):
1972
raise errors.AppendRevisionsOnlyViolation(self.base)
1974
def _gen_revision_history(self):
1975
"""Generate the revision history from last revision
1977
history = list(self.repository.iter_reverse_revision_history(
1978
self.last_revision()))
1982
def _write_revision_history(self, history):
1983
"""Factored out of set_revision_history.
1985
This performs the actual writing to disk, with format-specific checks.
1986
It is intended to be called by BzrBranch5.set_revision_history.
1988
if len(history) == 0:
1989
last_revision = 'null:'
1991
if history != self._lefthand_history(history[-1]):
1992
raise errors.NotLefthandHistory(history)
1993
last_revision = history[-1]
1994
if self._get_append_revisions_only():
1995
self._check_history_violation(last_revision)
1996
self._write_last_revision_info(len(history), last_revision)
1999
def append_revision(self, *revision_ids):
2000
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
2001
if len(revision_ids) == 0:
2003
prev_revno, prev_revision = self.last_revision_info()
2004
for revision in self.repository.get_revisions(revision_ids):
2005
if prev_revision == _mod_revision.NULL_REVISION:
2006
if revision.parent_ids != []:
2007
raise errors.NotLeftParentDescendant(self, prev_revision,
2008
revision.revision_id)
2010
if revision.parent_ids[0] != prev_revision:
2011
raise errors.NotLeftParentDescendant(self, prev_revision,
2012
revision.revision_id)
2013
prev_revision = revision.revision_id
2014
self.set_last_revision_info(prev_revno + len(revision_ids),
2018
def _set_parent_location(self, url):
2019
"""Set the parent branch"""
2020
self._set_config_location('parent_location', url, make_relative=True)
2023
def _get_parent_location(self):
2024
"""Set the parent branch"""
2025
return self._get_config_location('parent_location')
2027
def set_push_location(self, location):
2028
"""See Branch.set_push_location."""
2029
self._set_config_location('push_location', location)
2031
def set_bound_location(self, location):
2032
"""See Branch.set_push_location."""
2034
config = self.get_config()
2035
if location is None:
2036
if config.get_user_option('bound') != 'True':
2039
config.set_user_option('bound', 'False', warn_masked=True)
2042
self._set_config_location('bound_location', location,
2044
config.set_user_option('bound', 'True', warn_masked=True)
2047
def _get_bound_location(self, bound):
2048
"""Return the bound location in the config file.
2050
Return None if the bound parameter does not match"""
2051
config = self.get_config()
2052
config_bound = (config.get_user_option('bound') == 'True')
2053
if config_bound != bound:
2055
return self._get_config_location('bound_location', config=config)
2057
def get_bound_location(self):
2058
"""See Branch.set_push_location."""
2059
return self._get_bound_location(True)
2061
def get_old_bound_location(self):
2062
"""See Branch.get_old_bound_location"""
2063
return self._get_bound_location(False)
2065
def set_append_revisions_only(self, enabled):
2070
self.get_config().set_user_option('append_revisions_only', value,
2073
def _get_append_revisions_only(self):
2074
value = self.get_config().get_user_option('append_revisions_only')
2075
return value == 'True'
2077
def _synchronize_history(self, destination, revision_id):
2078
"""Synchronize last revision and revision history between branches.
2080
This version is most efficient when the destination is also a
2081
BzrBranch6, but works for BzrBranch5, as long as the destination's
2082
repository contains all the lefthand ancestors of the intended
2083
last_revision. If not, set_last_revision_info will fail.
2085
:param destination: The branch to copy the history into
2086
:param revision_id: The revision-id to truncate history at. May
2087
be None to copy complete history.
2089
if revision_id is None:
2090
revno, revision_id = self.last_revision_info()
2092
# To figure out the revno for a random revision, we need to build
2093
# the revision history, and count its length.
2094
# We don't care about the order, just how long it is.
2095
# Alternatively, we could start at the current location, and count
2096
# backwards. But there is no guarantee that we will find it since
2097
# it may be a merged revision.
2098
revno = len(list(self.repository.iter_reverse_revision_history(
2100
destination.set_last_revision_info(revno, revision_id)
2102
def _make_tags(self):
2103
return BasicTags(self)
1336
def __init__(self, transport_server, transport_readonly_server, formats):
1337
self._transport_server = transport_server
1338
self._transport_readonly_server = transport_readonly_server
1339
self._formats = formats
1341
def adapt(self, test):
1342
result = TestSuite()
1343
for branch_format, bzrdir_format in self._formats:
1344
new_test = deepcopy(test)
1345
new_test.transport_server = self._transport_server
1346
new_test.transport_readonly_server = self._transport_readonly_server
1347
new_test.bzrdir_format = bzrdir_format
1348
new_test.branch_format = branch_format
1349
def make_new_test_id():
1350
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
1351
return lambda: new_id
1352
new_test.id = make_new_test_id()
1353
result.addTest(new_test)
2106
1357
######################################################################
2107
# results of operations
2110
class _Result(object):
2112
def _show_tag_conficts(self, to_file):
2113
if not getattr(self, 'tag_conflicts', None):
2115
to_file.write('Conflicting tags:\n')
2116
for name, value1, value2 in self.tag_conflicts:
2117
to_file.write(' %s\n' % (name, ))
2120
class PullResult(_Result):
2121
"""Result of a Branch.pull operation.
2123
:ivar old_revno: Revision number before pull.
2124
:ivar new_revno: Revision number after pull.
2125
:ivar old_revid: Tip revision id before pull.
2126
:ivar new_revid: Tip revision id after pull.
2127
:ivar source_branch: Source (local) branch object.
2128
:ivar master_branch: Master branch of the target, or None.
2129
:ivar target_branch: Target/destination branch object.
2133
# DEPRECATED: pull used to return the change in revno
2134
return self.new_revno - self.old_revno
2136
def report(self, to_file):
2137
if self.old_revid == self.new_revid:
2138
to_file.write('No revisions to pull.\n')
2140
to_file.write('Now on revision %d.\n' % self.new_revno)
2141
self._show_tag_conficts(to_file)
2144
class PushResult(_Result):
2145
"""Result of a Branch.push operation.
2147
:ivar old_revno: Revision number before push.
2148
:ivar new_revno: Revision number after push.
2149
:ivar old_revid: Tip revision id before push.
2150
:ivar new_revid: Tip revision id after push.
2151
:ivar source_branch: Source branch object.
2152
:ivar master_branch: Master branch of the target, or None.
2153
:ivar target_branch: Target/destination branch object.
2157
# DEPRECATED: push used to return the change in revno
2158
return self.new_revno - self.old_revno
2160
def report(self, to_file):
2161
"""Write a human-readable description of the result."""
2162
if self.old_revid == self.new_revid:
2163
to_file.write('No new revisions to push.\n')
2165
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2166
self._show_tag_conficts(to_file)
2169
class BranchCheckResult(object):
2170
"""Results of checking branch consistency.
2175
def __init__(self, branch):
2176
self.branch = branch
2178
def report_results(self, verbose):
2179
"""Report the check results via trace.note.
2181
:param verbose: Requests more detailed display of what was checked,
2184
note('checked branch %s format %s',
2186
self.branch._format)
2189
class Converter5to6(object):
2190
"""Perform an in-place upgrade of format 5 to format 6"""
2192
def convert(self, branch):
2193
# Data for 5 and 6 can peacefully coexist.
2194
format = BzrBranchFormat6()
2195
new_branch = format.open(branch.bzrdir, _found=True)
2197
# Copy source data into target
2198
new_branch.set_last_revision_info(*branch.last_revision_info())
2199
new_branch.set_parent(branch.get_parent())
2200
new_branch.set_bound_location(branch.get_bound_location())
2201
new_branch.set_push_location(branch.get_push_location())
2203
# New branch has no tags by default
2204
new_branch.tags._set_tag_dict({})
2206
# Copying done; now update target format
2207
new_branch.control_files.put_utf8('format',
2208
format.get_format_string())
2210
# Clean up old files
2211
new_branch.control_files._transport.delete('revision-history')
2213
branch.set_parent(None)
2216
branch.set_bound_location(None)
1361
@deprecated_function(zero_eight)
1362
def ScratchBranch(*args, **kwargs):
1363
"""See bzrlib.bzrdir.ScratchDir."""
1364
d = ScratchDir(*args, **kwargs)
1365
return d.open_branch()
1368
@deprecated_function(zero_eight)
1369
def is_control_file(*args, **kwargs):
1370
"""See bzrlib.workingtree.is_control_file."""
1371
return bzrlib.workingtree.is_control_file(*args, **kwargs)