18
18
from copy import deepcopy
19
19
from cStringIO import StringIO
20
24
from unittest import TestSuite
21
25
from warnings import warn
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
28
import bzrlib.bzrdir as bzrdir
28
29
from bzrlib.config import TreeConfig
29
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
from bzrlib.delta import compare_trees
30
32
import bzrlib.errors as errors
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,
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
38
42
from bzrlib.lockable_files import LockableFiles, TransportLock
39
from bzrlib.symbol_versioning import (deprecated_function,
43
zero_eight, zero_nine,
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
45
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 (
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
48
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
99
119
def open(base, _unsupported=False):
100
"""Open the branch rooted at base.
120
"""Open the repository rooted at base.
102
For instance, if the branch is at URL/.bzr/branch,
103
Branch.open(URL) -> a Branch instance.
122
For instance, if the repository is at URL/.bzr/repository,
123
Repository.open(URL) -> a Repository instance.
105
125
control = bzrdir.BzrDir.open(base, _unsupported)
106
126
return control.open_branch(_unsupported)
138
158
warn('%s is deprecated' % self.setup_caching)
139
159
self.cache_root = cache_root
141
def get_config(self):
142
return bzrlib.config.BranchConfig(self)
144
161
def _get_nick(self):
145
return self.get_config().get_nickname()
162
cfg = self.tree_config()
163
return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
147
165
def _set_nick(self, nick):
148
self.get_config().set_user_option('nickname', nick)
166
cfg = self.tree_config()
167
cfg.set_option(nick, "nickname")
168
assert cfg.get_option("nickname") == nick
150
170
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])
272
260
def get_root_id(self):
273
261
"""Return the id of this branches root"""
274
262
raise NotImplementedError('get_root_id is abstract')
313
301
If self and other have not diverged, return a list of the revisions
314
302
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.
316
330
self_history = self.revision_history()
317
331
self_len = len(self_history)
402
416
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)
422
418
def get_push_location(self):
423
419
"""Return the None or the location to push this branch to."""
424
420
raise NotImplementedError('get_push_location is abstract')
461
457
revision_id: if not None, the revision history in the new branch will
462
458
be truncated to end with revision_id.
464
# for API compatibility, until 0.8 releases we provide the old api:
460
# for API compatability, until 0.8 releases we provide the old api:
465
461
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
466
462
# after 0.8 releases, the *args and **kwargs should be changed:
467
463
# def clone(self, to_bzrdir, revision_id=None):
469
465
kwargs.get('revision', None) or
470
466
kwargs.get('basis_branch', None) or
471
467
(len(args) and isinstance(args[0], basestring))):
472
# backwards compatibility api:
468
# backwards compatability api:
473
469
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
474
470
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
475
471
# get basis_branch
542
538
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)
575
541
class BranchFormat(object):
576
542
"""An encapsulation of the initialization and open routines for a format.
739
705
utf8_files = [('revision-history', ''),
740
706
('branch-name', ''),
742
control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
708
control_files = LockableFiles(branch_transport, 'lock', LockDir)
743
709
control_files.create_lock()
744
710
control_files.lock_write()
745
711
control_files.put_utf8('format', self.get_format_string())
764
730
format = BranchFormat.find_format(a_bzrdir)
765
731
assert format.__class__ == self.__class__
766
732
transport = a_bzrdir.get_branch_transport(None)
767
control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
733
control_files = LockableFiles(transport, 'lock', LockDir)
768
734
return BzrBranch5(_format=self,
769
735
_control_files=control_files,
770
736
a_bzrdir=a_bzrdir,
907
873
if (not relax_version_check
908
874
and not self._format.is_supported()):
909
raise errors.UnsupportedFormatError(format=fmt)
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'])
910
880
if deprecated_passed(transport):
911
881
warn("BzrBranch.__init__(transport=XXX...): The transport "
912
882
"parameter is deprecated as of bzr 0.8. "
1051
1021
# not really an object yet, and the transaction is for objects.
1052
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)
1054
1044
@needs_read_lock
1055
1045
def revision_history(self):
1056
1046
"""See Branch.revision_history."""
1068
1058
return list(history)
1070
1060
@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)
1100
1061
def update_revisions(self, other, stop_revision=None):
1101
1062
"""See Branch.update_revisions."""
1102
1063
other.lock_read()
1116
1077
if stop_revision in my_ancestry:
1117
1078
# last_revision is a descendant of stop_revision
1119
self.generate_revision_history(stop_revision, last_rev=last_rev,
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)
1156
1132
def get_parent(self):
1157
1133
"""See Branch.get_parent."""
1159
1135
_locs = ['parent', 'pull', 'x-pull']
1160
assert self.base[-1] == '/'
1161
1136
for l in _locs:
1163
parent = self.control_files.get(l).read().strip('\n')
1138
return self.control_files.get_utf8(l).read().strip('\n')
1164
1139
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)
1173
1143
def get_push_location(self):
1174
1144
"""See Branch.get_push_location."""
1175
push_loc = self.get_config().get_user_option('push_location')
1145
config = bzrlib.config.BranchConfig(self)
1146
push_loc = config.get_user_option('push_location')
1176
1147
return push_loc
1178
1149
def set_push_location(self, location):
1179
1150
"""See Branch.set_push_location."""
1180
self.get_config().set_user_option('push_location', location,
1151
config = bzrlib.config.LocationConfig(self.base)
1152
config.set_user_option('push_location', location)
1183
1154
@needs_write_lock
1184
1155
def set_parent(self, url):
1191
1162
if url is None:
1192
1163
self.control_files._transport.delete('parent')
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')
1165
self.control_files.put_utf8('parent', url + '\n')
1205
@deprecated_function(zero_nine)
1206
1167
def tree_config(self):
1207
"""DEPRECATED; call get_config instead.
1208
TreeConfig has become part of BranchConfig."""
1209
1168
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)
1398
1337
######################################################################
1402
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)
1403
1349
def is_control_file(*args, **kwargs):
1404
1350
"""See bzrlib.workingtree.is_control_file."""
1405
1351
return bzrlib.workingtree.is_control_file(*args, **kwargs)