18
18
from copy import deepcopy
19
19
from cStringIO import StringIO
24
20
from unittest import TestSuite
25
21
from warnings import warn
28
import bzrlib.bzrdir as bzrdir
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
29
28
from bzrlib.config import TreeConfig
30
29
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
from bzrlib.delta import compare_trees
32
30
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
31
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
32
HistoryMissing, InvalidRevisionId,
33
InvalidRevisionNumber, LockError, NoSuchFile,
34
NoSuchRevision, NoWorkingTree, NotVersionedError,
35
NotBranchError, UninitializableFormat,
36
UnlistableStore, UnlistableBranch,
42
38
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
39
from bzrlib.symbol_versioning import (deprecated_function,
43
zero_eight, zero_nine,
51
45
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
48
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
119
99
def open(base, _unsupported=False):
120
"""Open the repository rooted at base.
100
"""Open the branch rooted at base.
122
For instance, if the repository is at URL/.bzr/repository,
123
Repository.open(URL) -> a Repository instance.
102
For instance, if the branch is at URL/.bzr/branch,
103
Branch.open(URL) -> a Branch instance.
125
105
control = bzrdir.BzrDir.open(base, _unsupported)
126
106
return control.open_branch(_unsupported)
158
138
warn('%s is deprecated' % self.setup_caching)
159
139
self.cache_root = cache_root
141
def get_config(self):
142
return bzrlib.config.BranchConfig(self)
161
144
def _get_nick(self):
162
cfg = self.tree_config()
163
return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
145
return self.get_config().get_nickname()
165
147
def _set_nick(self, nick):
166
cfg = self.tree_config()
167
cfg.set_option(nick, "nickname")
168
assert cfg.get_option("nickname") == nick
148
self.get_config().set_user_option('nickname', nick)
170
150
nick = property(_get_nick, _set_nick)
233
def get_commit_builder(self, parents, config=None, timestamp=None,
234
timezone=None, committer=None, revprops=None,
236
"""Obtain a CommitBuilder for this branch.
238
:param parents: Revision ids of the parents of the new revision.
239
:param config: Optional configuration to use.
240
:param timestamp: Optional timestamp recorded for commit.
241
:param timezone: Optional timezone for timestamp.
242
:param committer: Optional committer to set for commit.
243
:param revprops: Optional dictionary of revision properties.
244
:param revision_id: Optional revision id.
248
config = self.get_config()
250
return self.repository.get_commit_builder(self, parents, config,
251
timestamp, timezone, committer, revprops, revision_id)
253
253
def get_master_branch(self):
254
254
"""Return the branch we are bound to.
260
def get_revision_delta(self, revno):
261
"""Return the delta for one revision.
263
The delta is relative to its mainline predecessor, or the
264
empty tree for revision 1.
266
assert isinstance(revno, int)
267
rh = self.revision_history()
268
if not (1 <= revno <= len(rh)):
269
raise InvalidRevisionNumber(revno)
270
return self.repository.get_revision_delta(rh[revno-1])
260
272
def get_root_id(self):
261
273
"""Return the id of this branches root"""
262
274
raise NotImplementedError('get_root_id is abstract')
301
313
If self and other have not diverged, return a list of the revisions
302
314
present in other, but missing from self.
304
>>> from bzrlib.workingtree import WorkingTree
305
>>> bzrlib.trace.silent = True
306
>>> d1 = bzrdir.ScratchDir()
307
>>> br1 = d1.open_branch()
308
>>> wt1 = d1.open_workingtree()
309
>>> d2 = bzrdir.ScratchDir()
310
>>> br2 = d2.open_branch()
311
>>> wt2 = d2.open_workingtree()
312
>>> br1.missing_revisions(br2)
314
>>> wt2.commit("lala!", rev_id="REVISION-ID-1")
315
>>> br1.missing_revisions(br2)
317
>>> br2.missing_revisions(br1)
319
>>> wt1.commit("lala!", rev_id="REVISION-ID-1")
320
>>> br1.missing_revisions(br2)
322
>>> wt2.commit("lala!", rev_id="REVISION-ID-2A")
323
>>> br1.missing_revisions(br2)
325
>>> wt1.commit("lala!", rev_id="REVISION-ID-2B")
326
>>> br1.missing_revisions(br2)
327
Traceback (most recent call last):
328
DivergedBranches: These branches have diverged. Try merge.
330
316
self_history = self.revision_history()
331
317
self_len = len(self_history)
416
402
raise NotImplementedError('get_parent is abstract')
404
def get_submit_branch(self):
405
"""Return the submit location of the branch.
407
This is the default location for bundle. The usual
408
pattern is that the user can override it by specifying a
411
return self.get_config().get_user_option('submit_branch')
413
def set_submit_branch(self, location):
414
"""Return the submit location of the branch.
416
This is the default location for bundle. The usual
417
pattern is that the user can override it by specifying a
420
self.get_config().set_user_option('submit_branch', location)
418
422
def get_push_location(self):
419
423
"""Return the None or the location to push this branch to."""
420
424
raise NotImplementedError('get_push_location is abstract')
457
461
revision_id: if not None, the revision history in the new branch will
458
462
be truncated to end with revision_id.
460
# for API compatability, until 0.8 releases we provide the old api:
464
# for API compatibility, until 0.8 releases we provide the old api:
461
465
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
462
466
# after 0.8 releases, the *args and **kwargs should be changed:
463
467
# def clone(self, to_bzrdir, revision_id=None):
465
469
kwargs.get('revision', None) or
466
470
kwargs.get('basis_branch', None) or
467
471
(len(args) and isinstance(args[0], basestring))):
468
# backwards compatability api:
472
# backwards compatibility api:
469
473
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
470
474
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
471
475
# get basis_branch
538
542
destination.set_parent(parent)
546
"""Check consistency of the branch.
548
In particular this checks that revisions given in the revision-history
549
do actually match up in the revision graph, and that they're all
550
present in the repository.
552
Callers will typically also want to check the repository.
554
:return: A BranchCheckResult.
556
mainline_parent_id = None
557
for revision_id in self.revision_history():
559
revision = self.repository.get_revision(revision_id)
560
except errors.NoSuchRevision, e:
561
raise errors.BzrCheckError("mainline revision {%s} not in repository"
563
# In general the first entry on the revision history has no parents.
564
# But it's not illegal for it to have parents listed; this can happen
565
# in imports from Arch when the parents weren't reachable.
566
if mainline_parent_id is not None:
567
if mainline_parent_id not in revision.parent_ids:
568
raise errors.BzrCheckError("previous revision {%s} not listed among "
570
% (mainline_parent_id, revision_id))
571
mainline_parent_id = revision_id
572
return BranchCheckResult(self)
541
575
class BranchFormat(object):
542
576
"""An encapsulation of the initialization and open routines for a format.
705
739
utf8_files = [('revision-history', ''),
706
740
('branch-name', ''),
708
control_files = LockableFiles(branch_transport, 'lock', LockDir)
742
control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
709
743
control_files.create_lock()
710
744
control_files.lock_write()
711
745
control_files.put_utf8('format', self.get_format_string())
730
764
format = BranchFormat.find_format(a_bzrdir)
731
765
assert format.__class__ == self.__class__
732
766
transport = a_bzrdir.get_branch_transport(None)
733
control_files = LockableFiles(transport, 'lock', LockDir)
767
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
734
768
return BzrBranch5(_format=self,
735
769
_control_files=control_files,
736
770
a_bzrdir=a_bzrdir,
873
907
if (not relax_version_check
874
908
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'])
909
raise errors.UnsupportedFormatError(format=fmt)
880
910
if deprecated_passed(transport):
881
911
warn("BzrBranch.__init__(transport=XXX...): The transport "
882
912
"parameter is deprecated as of bzr 0.8. "
1021
1051
# not really an object yet, and the transaction is for objects.
1022
1052
# 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)
1044
1054
@needs_read_lock
1045
1055
def revision_history(self):
1046
1056
"""See Branch.revision_history."""
1058
1068
return list(history)
1060
1070
@needs_write_lock
1071
def generate_revision_history(self, revision_id, last_rev=None,
1073
"""Create a new revision history that will finish with revision_id.
1075
:param revision_id: the new tip to use.
1076
:param last_rev: The previous last_revision. If not None, then this
1077
must be a ancestory of revision_id, or DivergedBranches is raised.
1078
:param other_branch: The other branch that DivergedBranches should
1079
raise with respect to.
1081
# stop_revision must be a descendant of last_revision
1082
stop_graph = self.repository.get_revision_graph(revision_id)
1083
if last_rev is not None and last_rev not in stop_graph:
1084
# our previous tip is not merged into stop_revision
1085
raise errors.DivergedBranches(self, other_branch)
1086
# make a new revision history from the graph
1087
current_rev_id = revision_id
1089
while current_rev_id not in (None, revision.NULL_REVISION):
1090
new_history.append(current_rev_id)
1091
current_rev_id_parents = stop_graph[current_rev_id]
1093
current_rev_id = current_rev_id_parents[0]
1095
current_rev_id = None
1096
new_history.reverse()
1097
self.set_revision_history(new_history)
1061
1100
def update_revisions(self, other, stop_revision=None):
1062
1101
"""See Branch.update_revisions."""
1063
1102
other.lock_read()
1077
1116
if stop_revision in my_ancestry:
1078
1117
# last_revision is a descendant of stop_revision
1080
# stop_revision must be a descendant of last_revision
1081
stop_graph = self.repository.get_revision_graph(stop_revision)
1082
if last_rev is not None and last_rev not in stop_graph:
1083
# our previous tip is not merged into stop_revision
1084
raise errors.DivergedBranches(self, other)
1085
# make a new revision history from the graph
1086
current_rev_id = stop_revision
1088
while current_rev_id not in (None, NULL_REVISION):
1089
new_history.append(current_rev_id)
1090
current_rev_id_parents = stop_graph[current_rev_id]
1092
current_rev_id = current_rev_id_parents[0]
1094
current_rev_id = None
1095
new_history.reverse()
1096
self.set_revision_history(new_history)
1119
self.generate_revision_history(stop_revision, last_rev=last_rev,
1132
1156
def get_parent(self):
1133
1157
"""See Branch.get_parent."""
1135
1159
_locs = ['parent', 'pull', 'x-pull']
1160
assert self.base[-1] == '/'
1136
1161
for l in _locs:
1138
return self.control_files.get_utf8(l).read().strip('\n')
1163
parent = self.control_files.get(l).read().strip('\n')
1139
1164
except NoSuchFile:
1166
# This is an old-format absolute path to a local branch
1167
# turn it into a url
1168
if parent.startswith('/'):
1169
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1170
return urlutils.join(self.base[:-1], parent)
1143
1173
def get_push_location(self):
1144
1174
"""See Branch.get_push_location."""
1145
config = bzrlib.config.BranchConfig(self)
1146
push_loc = config.get_user_option('push_location')
1175
push_loc = self.get_config().get_user_option('push_location')
1147
1176
return push_loc
1149
1178
def set_push_location(self, location):
1150
1179
"""See Branch.set_push_location."""
1151
config = bzrlib.config.LocationConfig(self.base)
1152
config.set_user_option('push_location', location)
1180
self.get_config().set_user_option('push_location', location,
1154
1183
@needs_write_lock
1155
1184
def set_parent(self, url):
1162
1191
if url is None:
1163
1192
self.control_files._transport.delete('parent')
1165
self.control_files.put_utf8('parent', url + '\n')
1194
if isinstance(url, unicode):
1196
url = url.encode('ascii')
1197
except UnicodeEncodeError:
1198
raise bzrlib.errors.InvalidURL(url,
1199
"Urls must be 7-bit ascii, "
1200
"use bzrlib.urlutils.escape")
1202
url = urlutils.relative_url(self.base, url)
1203
self.control_files.put('parent', url + '\n')
1205
@deprecated_function(zero_nine)
1167
1206
def tree_config(self):
1207
"""DEPRECATED; call get_config instead.
1208
TreeConfig has become part of BranchConfig."""
1168
1209
return TreeConfig(self)
1378
class BranchCheckResult(object):
1379
"""Results of checking branch consistency.
1384
def __init__(self, branch):
1385
self.branch = branch
1387
def report_results(self, verbose):
1388
"""Report the check results via trace.note.
1390
:param verbose: Requests more detailed display of what was checked,
1393
note('checked branch %s format %s',
1395
self.branch._format)
1337
1398
######################################################################
1341
1402
@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
1403
def is_control_file(*args, **kwargs):
1350
1404
"""See bzrlib.workingtree.is_control_file."""
1351
1405
return bzrlib.workingtree.is_control_file(*args, **kwargs)