15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from copy import deepcopy
19
18
from cStringIO import StringIO
24
from unittest import TestSuite
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
25
22
from warnings import warn
28
import bzrlib.bzrdir as bzrdir
29
from bzrlib.config import TreeConfig
28
config as _mod_config,
33
revision as _mod_revision,
40
from bzrlib.config import BranchConfig, TreeConfig
41
from bzrlib.lockable_files import LockableFiles, TransportLock
42
from bzrlib.tag import (
30
48
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
42
from bzrlib.lockable_files import LockableFiles, TransportLock
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
49
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
50
HistoryMissing, InvalidRevisionId,
51
InvalidRevisionNumber, LockError, NoSuchFile,
52
NoSuchRevision, 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,
51
64
from bzrlib.trace import mutter, note
52
from bzrlib.tree import EmptyTree, RevisionTree
53
from bzrlib.repository import Repository
54
from bzrlib.revision import (
59
from bzrlib.store import copy_all
60
from bzrlib.symbol_versioning import *
61
import bzrlib.transactions as transactions
62
from bzrlib.transport import Transport, get_transport
63
from bzrlib.tree import EmptyTree, RevisionTree
68
67
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
69
68
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
70
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
69
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
73
72
# TODO: Maybe include checks for common corruption of newlines, etc?
137
145
format, UnknownFormatError or UnsupportedFormatError are raised.
138
146
If there is one, it is returned, along with the unused portion of url.
140
control, relpath = bzrdir.BzrDir.open_containing(url)
148
control, relpath = bzrdir.BzrDir.open_containing(url,
141
150
return control.open_branch(), relpath
144
@deprecated_function(zero_eight)
145
def initialize(base):
146
"""Create a new working tree and branch, rooted at 'base' (url)
148
NOTE: This will soon be deprecated in favour of creation
151
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
153
def setup_caching(self, cache_root):
154
"""Subclasses that care about caching should override this, and set
155
up cached stores located under cache_root.
157
# seems to be unused, 2006-01-13 mbp
158
warn('%s is deprecated' % self.setup_caching)
159
self.cache_root = cache_root
152
def get_config(self):
153
return BranchConfig(self)
161
155
def _get_nick(self):
162
cfg = self.tree_config()
163
return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
156
return self.get_config().get_nickname()
165
158
def _set_nick(self, nick):
166
cfg = self.tree_config()
167
cfg.set_option(nick, "nickname")
168
assert cfg.get_option("nickname") == nick
159
self.get_config().set_user_option('nickname', nick, warn_masked=True)
170
161
nick = property(_get_nick, _set_nick)
172
163
def is_locked(self):
173
raise NotImplementedError('is_locked is abstract')
164
raise NotImplementedError(self.is_locked)
175
166
def lock_write(self):
176
raise NotImplementedError('lock_write is abstract')
167
raise NotImplementedError(self.lock_write)
178
169
def lock_read(self):
179
raise NotImplementedError('lock_read is abstract')
170
raise NotImplementedError(self.lock_read)
181
172
def unlock(self):
182
raise NotImplementedError('unlock is abstract')
173
raise NotImplementedError(self.unlock)
184
175
def peek_lock_mode(self):
185
176
"""Return lock mode for the Branch: 'r', 'w' or None"""
186
177
raise NotImplementedError(self.peek_lock_mode)
188
179
def get_physical_lock_status(self):
189
raise NotImplementedError('get_physical_lock_status is abstract')
180
raise NotImplementedError(self.get_physical_lock_status)
183
def get_revision_id_to_revno_map(self):
184
"""Return the revision_id => dotted revno map.
186
This will be regenerated on demand, but will be cached.
188
:return: A dictionary mapping revision_id => dotted revno.
189
This dictionary should not be modified by the caller.
191
if self._revision_id_to_revno_cache is not None:
192
mapping = self._revision_id_to_revno_cache
194
mapping = self._gen_revno_map()
195
self._cache_revision_id_to_revno(mapping)
196
# TODO: jam 20070417 Since this is being cached, should we be returning
198
# I would rather not, and instead just declare that users should not
199
# modify the return value.
202
def _gen_revno_map(self):
203
"""Create a new mapping from revision ids to dotted revnos.
205
Dotted revnos are generated based on the current tip in the revision
207
This is the worker function for get_revision_id_to_revno_map, which
208
just caches the return value.
210
:return: A dictionary mapping revision_id => dotted revno.
212
last_revision = self.last_revision()
213
revision_graph = self.repository.get_revision_graph(last_revision)
214
merge_sorted_revisions = tsort.merge_sort(
219
revision_id_to_revno = dict((rev_id, revno)
220
for seq_num, rev_id, depth, revno, end_of_merge
221
in merge_sorted_revisions)
222
return revision_id_to_revno
224
def leave_lock_in_place(self):
225
"""Tell this branch object not to release the physical lock when this
228
If lock_write doesn't return a token, then this method is not supported.
230
self.control_files.leave_in_place()
232
def dont_leave_lock_in_place(self):
233
"""Tell this branch object to release the physical lock when this
234
object is unlocked, even if it didn't originally acquire it.
236
If lock_write doesn't return a token, then this method is not supported.
238
self.control_files.dont_leave_in_place()
191
240
def abspath(self, name):
192
241
"""Return absolute filename for something in the branch
253
def get_master_branch(self):
299
def get_old_bound_location(self):
300
"""Return the URL of the branch we used to be bound to
302
raise errors.UpgradeRequired(self.base)
304
def get_commit_builder(self, parents, config=None, timestamp=None,
305
timezone=None, committer=None, revprops=None,
307
"""Obtain a CommitBuilder for this branch.
309
:param parents: Revision ids of the parents of the new revision.
310
:param config: Optional configuration to use.
311
:param timestamp: Optional timestamp recorded for commit.
312
:param timezone: Optional timezone for timestamp.
313
:param committer: Optional committer to set for commit.
314
:param revprops: Optional dictionary of revision properties.
315
:param revision_id: Optional revision id.
319
config = self.get_config()
321
return self.repository.get_commit_builder(self, parents, config,
322
timestamp, timezone, committer, revprops, revision_id)
324
def get_master_branch(self, possible_transports=None):
254
325
"""Return the branch we are bound to.
256
327
:return: Either a Branch, or None
331
def get_revision_delta(self, revno):
332
"""Return the delta for one revision.
334
The delta is relative to its mainline predecessor, or the
335
empty tree for revision 1.
337
assert isinstance(revno, int)
338
rh = self.revision_history()
339
if not (1 <= revno <= len(rh)):
340
raise InvalidRevisionNumber(revno)
341
return self.repository.get_revision_delta(rh[revno-1])
343
@deprecated_method(zero_sixteen)
260
344
def get_root_id(self):
261
"""Return the id of this branches root"""
262
raise NotImplementedError('get_root_id is abstract')
345
"""Return the id of this branches root
347
Deprecated: branches don't have root ids-- trees do.
348
Use basis_tree().get_root_id() instead.
350
raise NotImplementedError(self.get_root_id)
264
352
def print_file(self, file, revision_id):
265
353
"""Print `file` to stdout."""
266
raise NotImplementedError('print_file is abstract')
268
def append_revision(self, *revision_ids):
269
raise NotImplementedError('append_revision is abstract')
354
raise NotImplementedError(self.print_file)
271
356
def set_revision_history(self, rev_history):
272
raise NotImplementedError('set_revision_history is abstract')
357
raise NotImplementedError(self.set_revision_history)
359
def _cache_revision_history(self, rev_history):
360
"""Set the cached revision history to rev_history.
362
The revision_history method will use this cache to avoid regenerating
363
the revision history.
365
This API is semi-public; it only for use by subclasses, all other code
366
should consider it to be private.
368
self._revision_history_cache = rev_history
370
def _cache_revision_id_to_revno(self, revision_id_to_revno):
371
"""Set the cached revision_id => revno map to revision_id_to_revno.
373
This API is semi-public; it only for use by subclasses, all other code
374
should consider it to be private.
376
self._revision_id_to_revno_cache = revision_id_to_revno
378
def _clear_cached_state(self):
379
"""Clear any cached data on this branch, e.g. cached revision history.
381
This means the next call to revision_history will need to call
382
_gen_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 = None
388
self._revision_id_to_revno_cache = None
390
def _gen_revision_history(self):
391
"""Return sequence of revision hashes on to this branch.
393
Unlike revision_history, this method always regenerates or rereads the
394
revision history, i.e. it does not cache the result, so repeated calls
397
Concrete subclasses should override this instead of revision_history so
398
that subclasses do not need to deal with caching logic.
400
This API is semi-public; it only for use by subclasses, all other code
401
should consider it to be private.
403
raise NotImplementedError(self._gen_revision_history)
274
406
def revision_history(self):
275
"""Return sequence of revision hashes on to this branch."""
276
raise NotImplementedError('revision_history is abstract')
407
"""Return sequence of revision hashes on to this branch.
409
This method will cache the revision history for as long as it is safe to
412
if self._revision_history_cache is not None:
413
history = self._revision_history_cache
415
history = self._gen_revision_history()
416
self._cache_revision_history(history)
279
420
"""Return current revision number for this branch.
518
669
result.set_parent(self.bzrdir.root_transport.base)
522
def copy_content_into(self, destination, revision_id=None):
523
"""Copy the content of self into destination.
525
revision_id: if not None, the revision history in the new branch will
526
be truncated to end with revision_id.
672
def _synchronize_history(self, destination, revision_id):
673
"""Synchronize last revision and revision history between branches.
675
This version is most efficient when the destination is also a
676
BzrBranch5, but works for BzrBranch6 as long as the revision
677
history is the true lefthand parent history, and all of the revisions
678
are in the destination's repository. If not, set_revision_history
681
:param destination: The branch to copy the history into
682
:param revision_id: The revision-id to truncate history at. May
683
be None to copy complete history.
685
if revision_id == _mod_revision.NULL_REVISION:
528
687
new_history = self.revision_history()
529
if revision_id is not None:
688
if revision_id is not None and new_history != []:
689
revision_id = osutils.safe_revision_id(revision_id)
531
691
new_history = new_history[:new_history.index(revision_id) + 1]
532
692
except ValueError:
533
693
rev = self.repository.get_revision(revision_id)
534
694
new_history = rev.get_history(self.repository)[1:]
535
695
destination.set_revision_history(new_history)
536
parent = self.get_parent()
538
destination.set_parent(parent)
698
def copy_content_into(self, destination, revision_id=None):
699
"""Copy the content of self into destination.
701
revision_id: if not None, the revision history in the new branch will
702
be truncated to end with revision_id.
704
self._synchronize_history(destination, revision_id)
706
parent = self.get_parent()
707
except errors.InaccessibleParent, e:
708
mutter('parent was not accessible to copy: %s', e)
711
destination.set_parent(parent)
712
self.tags.merge_to(destination.tags)
716
"""Check consistency of the branch.
718
In particular this checks that revisions given in the revision-history
719
do actually match up in the revision graph, and that they're all
720
present in the repository.
722
Callers will typically also want to check the repository.
724
:return: A BranchCheckResult.
726
mainline_parent_id = None
727
for revision_id in self.revision_history():
729
revision = self.repository.get_revision(revision_id)
730
except errors.NoSuchRevision, e:
731
raise errors.BzrCheckError("mainline revision {%s} not in repository"
733
# In general the first entry on the revision history has no parents.
734
# But it's not illegal for it to have parents listed; this can happen
735
# in imports from Arch when the parents weren't reachable.
736
if mainline_parent_id is not None:
737
if mainline_parent_id not in revision.parent_ids:
738
raise errors.BzrCheckError("previous revision {%s} not listed among "
740
% (mainline_parent_id, revision_id))
741
mainline_parent_id = revision_id
742
return BranchCheckResult(self)
744
def _get_checkout_format(self):
745
"""Return the most suitable metadir for a checkout of this branch.
746
Weaves are used if this branch's repository uses weaves.
748
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
749
from bzrlib.repofmt import weaverepo
750
format = bzrdir.BzrDirMetaFormat1()
751
format.repository_format = weaverepo.RepositoryFormat7()
753
format = self.repository.bzrdir.checkout_metadir()
754
format.set_branch_format(self._format)
757
def create_checkout(self, to_location, revision_id=None,
759
"""Create a checkout of a branch.
761
:param to_location: The url to produce the checkout at
762
:param revision_id: The revision to check out
763
:param lightweight: If True, produce a lightweight checkout, otherwise,
764
produce a bound branch (heavyweight checkout)
765
:return: The tree of the created checkout
767
t = transport.get_transport(to_location)
770
format = self._get_checkout_format()
771
checkout = format.initialize_on_transport(t)
772
BranchReferenceFormat().initialize(checkout, self)
774
format = self._get_checkout_format()
775
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
776
to_location, force_new_tree=False, format=format)
777
checkout = checkout_branch.bzrdir
778
checkout_branch.bind(self)
779
# pull up to the specified revision_id to set the initial
780
# branch tip correctly, and seed it with history.
781
checkout_branch.pull(self, stop_revision=revision_id)
782
tree = checkout.create_workingtree(revision_id)
783
basis_tree = tree.basis_tree()
784
basis_tree.lock_read()
786
for path, file_id in basis_tree.iter_references():
787
reference_parent = self.reference_parent(file_id, path)
788
reference_parent.create_checkout(tree.abspath(path),
789
basis_tree.get_reference_revision(file_id, path),
795
def reference_parent(self, file_id, path):
796
"""Return the parent branch for a tree-reference file_id
797
:param file_id: The file_id of the tree reference
798
:param path: The path of the file_id in the tree
799
:return: A branch associated with the file_id
801
# FIXME should provide multiple branches, based on config
802
return Branch.open(self.bzrdir.root_transport.clone(path).base)
804
def supports_tags(self):
805
return self._format.supports_tags()
541
808
class BranchFormat(object):
624
941
def __str__(self):
625
942
return self.get_format_string().rstrip()
944
def supports_tags(self):
945
"""True if this format supports tags stored in the branch"""
946
return False # by default
948
# XXX: Probably doesn't really belong here -- mbp 20070212
949
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
951
branch_transport = a_bzrdir.get_branch_transport(self)
952
control_files = lockable_files.LockableFiles(branch_transport,
953
lock_filename, lock_class)
954
control_files.create_lock()
955
control_files.lock_write()
957
for filename, content in utf8_files:
958
control_files.put_utf8(filename, content)
960
control_files.unlock()
963
class BranchHooks(Hooks):
964
"""A dictionary mapping hook name to a list of callables for branch hooks.
966
e.g. ['set_rh'] Is the list of items to be called when the
967
set_revision_history function is invoked.
971
"""Create the default hooks.
973
These are all empty initially, because by default nothing should get
977
# Introduced in 0.15:
978
# invoked whenever the revision history has been set
979
# with set_revision_history. The api signature is
980
# (branch, revision_history), and the branch will
983
# invoked after a push operation completes.
984
# the api signature is
986
# containing the members
987
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
988
# where local is the local target branch or None, master is the target
989
# master branch, and the rest should be self explanatory. The source
990
# is read locked and the target branches write locked. Source will
991
# be the local low-latency branch.
992
self['post_push'] = []
993
# invoked after a pull operation completes.
994
# the api signature is
996
# containing the members
997
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
998
# where local is the local branch or None, master is the target
999
# master branch, and the rest should be self explanatory. The source
1000
# is read locked and the target branches write locked. The local
1001
# branch is the low-latency branch.
1002
self['post_pull'] = []
1003
# invoked before a commit operation takes place.
1004
# the api signature is
1005
# (local, master, old_revno, old_revid, future_revno, future_revid,
1006
# tree_delta, future_tree).
1007
# old_revid is NULL_REVISION for the first commit to a branch
1008
# tree_delta is a TreeDelta object describing changes from the basis
1009
# revision, hooks MUST NOT modify this delta
1010
# future_tree is an in-memory tree obtained from
1011
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1012
self['pre_commit'] = []
1013
# invoked after a commit operation completes.
1014
# the api signature is
1015
# (local, master, old_revno, old_revid, new_revno, new_revid)
1016
# old_revid is NULL_REVISION for the first commit to a branch.
1017
self['post_commit'] = []
1018
# invoked after a uncommit operation completes.
1019
# the api signature is
1020
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1021
# local is the local branch or None, master is the target branch,
1022
# and an empty branch recieves new_revno of 0, new_revid of None.
1023
self['post_uncommit'] = []
1026
# install the default hooks into the Branch class.
1027
Branch.hooks = BranchHooks()
628
1030
class BzrBranchFormat4(BranchFormat):
629
1031
"""Bzr branch format 4.
827
1266
it's writable, and can be accessed via the normal filesystem API.
830
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
831
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1269
def __init__(self, _format=None,
832
1270
_control_files=None, a_bzrdir=None, _repository=None):
833
"""Create new branch object at a particular location.
835
transport -- A Transport object, defining how to access files.
837
init -- If True, create new control files in a previously
838
unversioned directory. If False, the branch must already
841
relax_version_check -- If true, the usual check for the branch
842
version is not applied. This is intended only for
843
upgrade/recovery type use; it's not guaranteed that
844
all operations will work on old format branches.
1271
"""Create new branch object at a particular location."""
1272
Branch.__init__(self)
846
1273
if a_bzrdir is None:
847
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1274
raise ValueError('a_bzrdir must be supplied')
849
1276
self.bzrdir = a_bzrdir
850
self._transport = self.bzrdir.transport.clone('..')
851
self._base = self._transport.base
1277
# self._transport used to point to the directory containing the
1278
# control directory, but was not used - now it's just the transport
1279
# for the branch control files. mbp 20070212
1280
self._base = self.bzrdir.transport.clone('..').base
852
1281
self._format = _format
853
1282
if _control_files is None:
854
raise BzrBadParameterMissing('_control_files')
1283
raise ValueError('BzrBranch _control_files is None')
855
1284
self.control_files = _control_files
856
if deprecated_passed(init):
857
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
858
"deprecated as of bzr 0.8. Please use Branch.create().",
862
# this is slower than before deprecation, oh well never mind.
864
self._initialize(transport.base)
865
self._check_format(_format)
866
if deprecated_passed(relax_version_check):
867
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
868
"relax_version_check parameter is deprecated as of bzr 0.8. "
869
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
873
if (not relax_version_check
874
and not self._format.is_supported()):
875
raise errors.UnsupportedFormatError(
876
'sorry, branch format %r not supported' % fmt,
877
['use a different bzr version',
878
'or remove the .bzr directory'
879
' and "bzr init" again'])
880
if deprecated_passed(transport):
881
warn("BzrBranch.__init__(transport=XXX...): The transport "
882
"parameter is deprecated as of bzr 0.8. "
883
"Please use Branch.open, or bzrdir.open_branch().",
1285
self._transport = _control_files._transport
886
1286
self.repository = _repository
888
1288
def __str__(self):
993
1352
"""See Branch.print_file."""
994
1353
return self.repository.print_file(file, revision_id)
997
def append_revision(self, *revision_ids):
998
"""See Branch.append_revision."""
999
for revision_id in revision_ids:
1000
mutter("add {%s} to revision-history" % revision_id)
1001
rev_history = self.revision_history()
1002
rev_history.extend(revision_ids)
1003
self.set_revision_history(rev_history)
1355
def _write_revision_history(self, history):
1356
"""Factored out of set_revision_history.
1358
This performs the actual writing to disk.
1359
It is intended to be called by BzrBranch5.set_revision_history."""
1360
self.control_files.put_bytes(
1361
'revision-history', '\n'.join(history))
1005
1363
@needs_write_lock
1006
1364
def set_revision_history(self, rev_history):
1007
1365
"""See Branch.set_revision_history."""
1008
self.control_files.put_utf8(
1009
'revision-history', '\n'.join(rev_history))
1010
transaction = self.get_transaction()
1011
history = transaction.map.find_revision_history()
1012
if history is not None:
1013
# update the revision history in the identity map.
1014
history[:] = list(rev_history)
1015
# this call is disabled because revision_history is
1016
# not really an object yet, and the transaction is for objects.
1017
# transaction.register_dirty(history)
1019
transaction.map.add_revision_history(rev_history)
1020
# this call is disabled because revision_history is
1021
# not really an object yet, and the transaction is for objects.
1022
# transaction.register_clean(history)
1024
def get_revision_delta(self, revno):
1025
"""Return the delta for one revision.
1027
The delta is relative to its mainline predecessor, or the
1028
empty tree for revision 1.
1030
assert isinstance(revno, int)
1031
rh = self.revision_history()
1032
if not (1 <= revno <= len(rh)):
1033
raise InvalidRevisionNumber(revno)
1035
# revno is 1-based; list is 0-based
1037
new_tree = self.repository.revision_tree(rh[revno-1])
1039
old_tree = EmptyTree()
1041
old_tree = self.repository.revision_tree(rh[revno-2])
1042
return compare_trees(old_tree, new_tree)
1045
def revision_history(self):
1046
"""See Branch.revision_history."""
1047
transaction = self.get_transaction()
1048
history = transaction.map.find_revision_history()
1049
if history is not None:
1050
mutter("cache hit for revision-history in %s", self)
1051
return list(history)
1052
history = [l.rstrip('\r\n') for l in
1053
self.control_files.get_utf8('revision-history').readlines()]
1054
transaction.map.add_revision_history(history)
1055
# this call is disabled because revision_history is
1056
# not really an object yet, and the transaction is for objects.
1057
# transaction.register_clean(history, precious=True)
1058
return list(history)
1366
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1367
self._clear_cached_state()
1368
self._write_revision_history(rev_history)
1369
self._cache_revision_history(rev_history)
1370
for hook in Branch.hooks['set_rh']:
1371
hook(self, rev_history)
1374
def set_last_revision_info(self, revno, revision_id):
1375
"""Set the last revision of this branch.
1377
The caller is responsible for checking that the revno is correct
1378
for this revision id.
1380
It may be possible to set the branch last revision to an id not
1381
present in the repository. However, branches can also be
1382
configured to check constraints on history, in which case this may not
1385
revision_id = osutils.safe_revision_id(revision_id)
1386
history = self._lefthand_history(revision_id)
1387
assert len(history) == revno, '%d != %d' % (len(history), revno)
1388
self.set_revision_history(history)
1390
def _gen_revision_history(self):
1391
history = self.control_files.get('revision-history').read().split('\n')
1392
if history[-1:] == ['']:
1393
# There shouldn't be a trailing newline, but just in case.
1397
def _lefthand_history(self, revision_id, last_rev=None,
1399
# stop_revision must be a descendant of last_revision
1400
stop_graph = self.repository.get_revision_graph(revision_id)
1401
if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1402
and last_rev not in stop_graph):
1403
# our previous tip is not merged into stop_revision
1404
raise errors.DivergedBranches(self, other_branch)
1405
# make a new revision history from the graph
1406
current_rev_id = revision_id
1408
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1409
new_history.append(current_rev_id)
1410
current_rev_id_parents = stop_graph[current_rev_id]
1412
current_rev_id = current_rev_id_parents[0]
1414
current_rev_id = None
1415
new_history.reverse()
1419
def generate_revision_history(self, revision_id, last_rev=None,
1421
"""Create a new revision history that will finish with revision_id.
1423
:param revision_id: the new tip to use.
1424
:param last_rev: The previous last_revision. If not None, then this
1425
must be a ancestory of revision_id, or DivergedBranches is raised.
1426
:param other_branch: The other branch that DivergedBranches should
1427
raise with respect to.
1429
revision_id = osutils.safe_revision_id(revision_id)
1430
self.set_revision_history(self._lefthand_history(revision_id,
1431
last_rev, other_branch))
1060
1433
@needs_write_lock
1061
1434
def update_revisions(self, other, stop_revision=None):
1101
1460
"""See Branch.basis_tree."""
1102
1461
return self.repository.revision_tree(self.last_revision())
1104
@deprecated_method(zero_eight)
1105
def working_tree(self):
1106
"""Create a Working tree object for this branch."""
1107
from bzrlib.workingtree import WorkingTree
1108
from bzrlib.transport.local import LocalTransport
1109
if (self.base.find('://') != -1 or
1110
not isinstance(self._transport, LocalTransport)):
1111
raise NoWorkingTree(self.base)
1112
return self.bzrdir.open_workingtree()
1114
1463
@needs_write_lock
1115
def pull(self, source, overwrite=False, stop_revision=None):
1116
"""See Branch.pull."""
1464
def pull(self, source, overwrite=False, stop_revision=None,
1465
_hook_master=None, run_hooks=True):
1468
:param _hook_master: Private parameter - set the branch to
1469
be supplied as the master to push hooks.
1470
:param run_hooks: Private parameter - if false, this branch
1471
is being called because it's the master of the primary branch,
1472
so it should not run its hooks.
1474
result = PullResult()
1475
result.source_branch = source
1476
result.target_branch = self
1117
1477
source.lock_read()
1119
old_count = len(self.revision_history())
1479
result.old_revno, result.old_revid = self.last_revision_info()
1121
self.update_revisions(source,stop_revision)
1481
self.update_revisions(source, stop_revision)
1122
1482
except DivergedBranches:
1123
1483
if not overwrite:
1126
self.set_revision_history(source.revision_history())
1127
new_count = len(self.revision_history())
1128
return new_count - old_count
1486
if stop_revision is None:
1487
stop_revision = source.last_revision()
1488
self.generate_revision_history(stop_revision)
1489
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1490
result.new_revno, result.new_revid = self.last_revision_info()
1492
result.master_branch = _hook_master
1493
result.local_branch = self
1495
result.master_branch = self
1496
result.local_branch = None
1498
for hook in Branch.hooks['post_pull']:
1130
1501
source.unlock()
1132
def get_parent(self):
1133
"""See Branch.get_parent."""
1504
def _get_parent_location(self):
1135
1505
_locs = ['parent', 'pull', 'x-pull']
1136
1506
for l in _locs:
1138
return self.control_files.get_utf8(l).read().strip('\n')
1508
return self.control_files.get(l).read().strip('\n')
1139
1509
except NoSuchFile:
1143
def get_push_location(self):
1144
"""See Branch.get_push_location."""
1145
config = bzrlib.config.BranchConfig(self)
1146
push_loc = config.get_user_option('push_location')
1514
def push(self, target, overwrite=False, stop_revision=None,
1515
_override_hook_source_branch=None):
1518
This is the basic concrete implementation of push()
1520
:param _override_hook_source_branch: If specified, run
1521
the hooks passing this Branch as the source, rather than self.
1522
This is for use of RemoteBranch, where push is delegated to the
1523
underlying vfs-based Branch.
1525
# TODO: Public option to disable running hooks - should be trivial but
1529
result = self._push_with_bound_branches(target, overwrite,
1531
_override_hook_source_branch=_override_hook_source_branch)
1536
def _push_with_bound_branches(self, target, overwrite,
1538
_override_hook_source_branch=None):
1539
"""Push from self into target, and into target's master if any.
1541
This is on the base BzrBranch class even though it doesn't support
1542
bound branches because the *target* might be bound.
1545
if _override_hook_source_branch:
1546
result.source_branch = _override_hook_source_branch
1547
for hook in Branch.hooks['post_push']:
1550
bound_location = target.get_bound_location()
1551
if bound_location and target.base != bound_location:
1552
# there is a master branch.
1554
# XXX: Why the second check? Is it even supported for a branch to
1555
# be bound to itself? -- mbp 20070507
1556
master_branch = target.get_master_branch()
1557
master_branch.lock_write()
1559
# push into the master from this branch.
1560
self._basic_push(master_branch, overwrite, stop_revision)
1561
# and push into the target branch from this. Note that we push from
1562
# this branch again, because its considered the highest bandwidth
1564
result = self._basic_push(target, overwrite, stop_revision)
1565
result.master_branch = master_branch
1566
result.local_branch = target
1570
master_branch.unlock()
1573
result = self._basic_push(target, overwrite, stop_revision)
1574
# TODO: Why set master_branch and local_branch if there's no
1575
# binding? Maybe cleaner to just leave them unset? -- mbp
1577
result.master_branch = target
1578
result.local_branch = None
1582
def _basic_push(self, target, overwrite, stop_revision):
1583
"""Basic implementation of push without bound branches or hooks.
1585
Must be called with self read locked and target write locked.
1587
result = PushResult()
1588
result.source_branch = self
1589
result.target_branch = target
1590
result.old_revno, result.old_revid = target.last_revision_info()
1592
target.update_revisions(self, stop_revision)
1593
except DivergedBranches:
1597
target.set_revision_history(self.revision_history())
1598
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1599
result.new_revno, result.new_revid = target.last_revision_info()
1602
def get_parent(self):
1603
"""See Branch.get_parent."""
1605
assert self.base[-1] == '/'
1606
parent = self._get_parent_location()
1609
# This is an old-format absolute path to a local branch
1610
# turn it into a url
1611
if parent.startswith('/'):
1612
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1614
return urlutils.join(self.base[:-1], parent)
1615
except errors.InvalidURLJoin, e:
1616
raise errors.InaccessibleParent(parent, self.base)
1149
1618
def set_push_location(self, location):
1150
1619
"""See Branch.set_push_location."""
1151
config = bzrlib.config.LocationConfig(self.base)
1152
config.set_user_option('push_location', location)
1620
self.get_config().set_user_option(
1621
'push_location', location,
1622
store=_mod_config.STORE_LOCATION_NORECURSE)
1154
1624
@needs_write_lock
1155
1625
def set_parent(self, url):
1288
1781
return self.set_bound_location(None)
1290
1783
@needs_write_lock
1784
def update(self, possible_transports=None):
1292
1785
"""Synchronise this branch with the master branch if any.
1294
1787
:return: None or the last_revision that was pivoted out during the
1297
master = self.get_master_branch()
1790
master = self.get_master_branch(possible_transports)
1298
1791
if master is not None:
1299
old_tip = self.last_revision()
1792
old_tip = _mod_revision.ensure_null(self.last_revision())
1300
1793
self.pull(master, overwrite=True)
1301
if old_tip in self.repository.get_ancestry(self.last_revision()):
1794
if self.repository.get_graph().is_ancestor(old_tip,
1795
_mod_revision.ensure_null(self.last_revision())):
1307
class BranchTestProviderAdapter(object):
1308
"""A tool to generate a suite testing multiple branch formats at once.
1310
This is done by copying the test once for each transport and injecting
1311
the transport_server, transport_readonly_server, and branch_format
1312
classes into each copy. Each copy is also given a new id() to make it
1801
class BzrBranchExperimental(BzrBranch5):
1802
"""Bzr experimental branch format
1805
- a revision-history file.
1807
- a lock dir guarding the branch itself
1808
- all of this stored in a branch/ subdirectory
1809
- works with shared repositories.
1810
- a tag dictionary in the branch
1812
This format is new in bzr 0.15, but shouldn't be used for real data,
1815
This class acts as it's own BranchFormat.
1316
def __init__(self, transport_server, transport_readonly_server, formats):
1317
self._transport_server = transport_server
1318
self._transport_readonly_server = transport_readonly_server
1319
self._formats = formats
1321
def adapt(self, test):
1322
result = TestSuite()
1323
for branch_format, bzrdir_format in self._formats:
1324
new_test = deepcopy(test)
1325
new_test.transport_server = self._transport_server
1326
new_test.transport_readonly_server = self._transport_readonly_server
1327
new_test.bzrdir_format = bzrdir_format
1328
new_test.branch_format = branch_format
1329
def make_new_test_id():
1330
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
1331
return lambda: new_id
1332
new_test.id = make_new_test_id()
1333
result.addTest(new_test)
1818
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1821
def get_format_string(cls):
1822
"""See BranchFormat.get_format_string()."""
1823
return "Bazaar-NG branch format experimental\n"
1826
def get_format_description(cls):
1827
"""See BranchFormat.get_format_description()."""
1828
return "Experimental branch format"
1831
def get_reference(cls, a_bzrdir):
1832
"""Get the target reference of the branch in a_bzrdir.
1834
format probing must have been completed before calling
1835
this method - it is assumed that the format of the branch
1836
in a_bzrdir is correct.
1838
:param a_bzrdir: The bzrdir to get the branch data from.
1839
:return: None if the branch is not a reference branch.
1844
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1846
branch_transport = a_bzrdir.get_branch_transport(cls)
1847
control_files = lockable_files.LockableFiles(branch_transport,
1848
lock_filename, lock_class)
1849
control_files.create_lock()
1850
control_files.lock_write()
1852
for filename, content in utf8_files:
1853
control_files.put_utf8(filename, content)
1855
control_files.unlock()
1858
def initialize(cls, a_bzrdir):
1859
"""Create a branch of this format in a_bzrdir."""
1860
utf8_files = [('format', cls.get_format_string()),
1861
('revision-history', ''),
1862
('branch-name', ''),
1865
cls._initialize_control_files(a_bzrdir, utf8_files,
1866
'lock', lockdir.LockDir)
1867
return cls.open(a_bzrdir, _found=True)
1870
def open(cls, a_bzrdir, _found=False):
1871
"""Return the branch object for a_bzrdir
1873
_found is a private parameter, do not use it. It is used to indicate
1874
if format probing has already be done.
1877
format = BranchFormat.find_format(a_bzrdir)
1878
assert format.__class__ == cls
1879
transport = a_bzrdir.get_branch_transport(None)
1880
control_files = lockable_files.LockableFiles(transport, 'lock',
1882
return cls(_format=cls,
1883
_control_files=control_files,
1885
_repository=a_bzrdir.find_repository())
1888
def is_supported(cls):
1891
def _make_tags(self):
1892
return BasicTags(self)
1895
def supports_tags(cls):
1899
BranchFormat.register_format(BzrBranchExperimental)
1902
class BzrBranch6(BzrBranch5):
1905
def last_revision_info(self):
1906
revision_string = self.control_files.get('last-revision').read()
1907
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1908
revision_id = cache_utf8.get_cached_utf8(revision_id)
1910
return revno, revision_id
1912
def last_revision(self):
1913
"""Return last revision id, or None"""
1914
revision_id = self.last_revision_info()[1]
1917
def _write_last_revision_info(self, revno, revision_id):
1918
"""Simply write out the revision id, with no checks.
1920
Use set_last_revision_info to perform this safely.
1922
Does not update the revision_history cache.
1923
Intended to be called by set_last_revision_info and
1924
_write_revision_history.
1926
if revision_id is None:
1927
revision_id = 'null:'
1928
out_string = '%d %s\n' % (revno, revision_id)
1929
self.control_files.put_bytes('last-revision', out_string)
1932
def set_last_revision_info(self, revno, revision_id):
1933
revision_id = osutils.safe_revision_id(revision_id)
1934
if self._get_append_revisions_only():
1935
self._check_history_violation(revision_id)
1936
self._write_last_revision_info(revno, revision_id)
1937
self._clear_cached_state()
1939
def _check_history_violation(self, revision_id):
1940
last_revision = _mod_revision.ensure_null(self.last_revision())
1941
if _mod_revision.is_null(last_revision):
1943
if last_revision not in self._lefthand_history(revision_id):
1944
raise errors.AppendRevisionsOnlyViolation(self.base)
1946
def _gen_revision_history(self):
1947
"""Generate the revision history from last revision
1949
history = list(self.repository.iter_reverse_revision_history(
1950
self.last_revision()))
1954
def _write_revision_history(self, history):
1955
"""Factored out of set_revision_history.
1957
This performs the actual writing to disk, with format-specific checks.
1958
It is intended to be called by BzrBranch5.set_revision_history.
1960
if len(history) == 0:
1961
last_revision = 'null:'
1963
if history != self._lefthand_history(history[-1]):
1964
raise errors.NotLefthandHistory(history)
1965
last_revision = history[-1]
1966
if self._get_append_revisions_only():
1967
self._check_history_violation(last_revision)
1968
self._write_last_revision_info(len(history), last_revision)
1971
def _set_parent_location(self, url):
1972
"""Set the parent branch"""
1973
self._set_config_location('parent_location', url, make_relative=True)
1976
def _get_parent_location(self):
1977
"""Set the parent branch"""
1978
return self._get_config_location('parent_location')
1980
def set_push_location(self, location):
1981
"""See Branch.set_push_location."""
1982
self._set_config_location('push_location', location)
1984
def set_bound_location(self, location):
1985
"""See Branch.set_push_location."""
1987
config = self.get_config()
1988
if location is None:
1989
if config.get_user_option('bound') != 'True':
1992
config.set_user_option('bound', 'False', warn_masked=True)
1995
self._set_config_location('bound_location', location,
1997
config.set_user_option('bound', 'True', warn_masked=True)
2000
def _get_bound_location(self, bound):
2001
"""Return the bound location in the config file.
2003
Return None if the bound parameter does not match"""
2004
config = self.get_config()
2005
config_bound = (config.get_user_option('bound') == 'True')
2006
if config_bound != bound:
2008
return self._get_config_location('bound_location', config=config)
2010
def get_bound_location(self):
2011
"""See Branch.set_push_location."""
2012
return self._get_bound_location(True)
2014
def get_old_bound_location(self):
2015
"""See Branch.get_old_bound_location"""
2016
return self._get_bound_location(False)
2018
def set_append_revisions_only(self, enabled):
2023
self.get_config().set_user_option('append_revisions_only', value,
2026
def _get_append_revisions_only(self):
2027
value = self.get_config().get_user_option('append_revisions_only')
2028
return value == 'True'
2030
def _synchronize_history(self, destination, revision_id):
2031
"""Synchronize last revision and revision history between branches.
2033
This version is most efficient when the destination is also a
2034
BzrBranch6, but works for BzrBranch5, as long as the destination's
2035
repository contains all the lefthand ancestors of the intended
2036
last_revision. If not, set_last_revision_info will fail.
2038
:param destination: The branch to copy the history into
2039
:param revision_id: The revision-id to truncate history at. May
2040
be None to copy complete history.
2042
source_revno, source_revision_id = self.last_revision_info()
2043
if revision_id is None:
2044
revno, revision_id = source_revno, source_revision_id
2045
elif source_revision_id == revision_id:
2046
# we know the revno without needing to walk all of history
2047
revno = source_revno
2049
# To figure out the revno for a random revision, we need to build
2050
# the revision history, and count its length.
2051
# We don't care about the order, just how long it is.
2052
# Alternatively, we could start at the current location, and count
2053
# backwards. But there is no guarantee that we will find it since
2054
# it may be a merged revision.
2055
revno = len(list(self.repository.iter_reverse_revision_history(
2057
destination.set_last_revision_info(revno, revision_id)
2059
def _make_tags(self):
2060
return BasicTags(self)
1337
2063
######################################################################
1341
@deprecated_function(zero_eight)
1342
def ScratchBranch(*args, **kwargs):
1343
"""See bzrlib.bzrdir.ScratchDir."""
1344
d = ScratchDir(*args, **kwargs)
1345
return d.open_branch()
1348
@deprecated_function(zero_eight)
1349
def is_control_file(*args, **kwargs):
1350
"""See bzrlib.workingtree.is_control_file."""
1351
return bzrlib.workingtree.is_control_file(*args, **kwargs)
2064
# results of operations
2067
class _Result(object):
2069
def _show_tag_conficts(self, to_file):
2070
if not getattr(self, 'tag_conflicts', None):
2072
to_file.write('Conflicting tags:\n')
2073
for name, value1, value2 in self.tag_conflicts:
2074
to_file.write(' %s\n' % (name, ))
2077
class PullResult(_Result):
2078
"""Result of a Branch.pull operation.
2080
:ivar old_revno: Revision number before pull.
2081
:ivar new_revno: Revision number after pull.
2082
:ivar old_revid: Tip revision id before pull.
2083
:ivar new_revid: Tip revision id after pull.
2084
:ivar source_branch: Source (local) branch object.
2085
:ivar master_branch: Master branch of the target, or None.
2086
:ivar target_branch: Target/destination branch object.
2090
# DEPRECATED: pull used to return the change in revno
2091
return self.new_revno - self.old_revno
2093
def report(self, to_file):
2094
if self.old_revid == self.new_revid:
2095
to_file.write('No revisions to pull.\n')
2097
to_file.write('Now on revision %d.\n' % self.new_revno)
2098
self._show_tag_conficts(to_file)
2101
class PushResult(_Result):
2102
"""Result of a Branch.push operation.
2104
:ivar old_revno: Revision number before push.
2105
:ivar new_revno: Revision number after push.
2106
:ivar old_revid: Tip revision id before push.
2107
:ivar new_revid: Tip revision id after push.
2108
:ivar source_branch: Source branch object.
2109
:ivar master_branch: Master branch of the target, or None.
2110
:ivar target_branch: Target/destination branch object.
2114
# DEPRECATED: push used to return the change in revno
2115
return self.new_revno - self.old_revno
2117
def report(self, to_file):
2118
"""Write a human-readable description of the result."""
2119
if self.old_revid == self.new_revid:
2120
to_file.write('No new revisions to push.\n')
2122
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2123
self._show_tag_conficts(to_file)
2126
class BranchCheckResult(object):
2127
"""Results of checking branch consistency.
2132
def __init__(self, branch):
2133
self.branch = branch
2135
def report_results(self, verbose):
2136
"""Report the check results via trace.note.
2138
:param verbose: Requests more detailed display of what was checked,
2141
note('checked branch %s format %s',
2143
self.branch._format)
2146
class Converter5to6(object):
2147
"""Perform an in-place upgrade of format 5 to format 6"""
2149
def convert(self, branch):
2150
# Data for 5 and 6 can peacefully coexist.
2151
format = BzrBranchFormat6()
2152
new_branch = format.open(branch.bzrdir, _found=True)
2154
# Copy source data into target
2155
new_branch.set_last_revision_info(*branch.last_revision_info())
2156
new_branch.set_parent(branch.get_parent())
2157
new_branch.set_bound_location(branch.get_bound_location())
2158
new_branch.set_push_location(branch.get_push_location())
2160
# New branch has no tags by default
2161
new_branch.tags._set_tag_dict({})
2163
# Copying done; now update target format
2164
new_branch.control_files.put_utf8('format',
2165
format.get_format_string())
2167
# Clean up old files
2168
new_branch.control_files._transport.delete('revision-history')
2170
branch.set_parent(None)
2173
branch.set_bound_location(None)