25
25
sha_file, appendpath, file_kind
27
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId, \
28
DivergedBranches, NotBranchError
27
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
29
29
from bzrlib.textui import show_status
30
30
from bzrlib.revision import Revision
31
from bzrlib.xml import unpack_xml
31
32
from bzrlib.delta import compare_trees
32
33
from bzrlib.tree import EmptyTree, RevisionTree
50
50
def find_branch(f, **args):
51
51
if f and (f.startswith('http://') or f.startswith('https://')):
52
from bzrlib.remotebranch import RemoteBranch
53
return RemoteBranch(f, **args)
53
return remotebranch.RemoteBranch(f, **args)
55
55
return Branch(f, **args)
58
58
def find_cached_branch(f, cache_root, **args):
59
from bzrlib.remotebranch import RemoteBranch
59
from remotebranch import RemoteBranch
60
60
br = find_branch(f, **args)
61
61
def cacheify(br, store_name):
62
from bzrlib.meta_store import CachedStore
62
from meta_store import CachedStore
63
63
cache_path = os.path.join(cache_root, store_name)
64
64
os.mkdir(cache_path)
65
65
new_store = CachedStore(getattr(br, store_name), cache_path)
127
128
head, tail = os.path.split(f)
129
130
# reached the root, whatever that may be
130
raise NotBranchError('%s is not in a branch' % orig_f)
131
raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
136
# XXX: move into bzrlib.errors; subclass BzrError
137
class DivergedBranches(Exception):
138
def __init__(self, branch1, branch2):
139
self.branch1 = branch1
140
self.branch2 = branch2
141
Exception.__init__(self, "These branches have diverged.")
136
144
######################################################################
165
173
def __init__(self, base, init=False, find_root=True):
166
174
"""Create new branch object at a particular location.
168
base -- Base directory for the branch. May be a file:// url.
176
base -- Base directory for the branch.
170
178
init -- If True, create new control files in a previously
171
179
unversioned directory. If False, the branch must already
185
193
self.base = find_branch_root(base)
187
if base.startswith("file://"):
189
195
self.base = os.path.realpath(base)
190
196
if not isdir(self.controlfilename('.')):
197
from errors import NotBranchError
191
198
raise NotBranchError("not a bzr branch: %s" % quotefn(base),
192
199
['use "bzr init" to initialize a new working tree',
193
200
'current bzr can only operate from top-of-tree'])
208
215
def __del__(self):
209
216
if self._lock_mode or self._lock:
210
from bzrlib.warnings import warn
217
from warnings import warn
211
218
warn("branch %r was not explicitly unlocked" % self)
212
219
self._lock.unlock()
214
222
def lock_write(self):
215
223
if self._lock_mode:
216
224
if self._lock_mode != 'w':
217
from bzrlib.errors import LockError
225
from errors import LockError
218
226
raise LockError("can't upgrade to a write lock from %r" %
220
228
self._lock_count += 1
295
303
def _make_control(self):
296
304
from bzrlib.inventory import Inventory
305
from bzrlib.xml import pack_xml
298
307
os.mkdir(self.controlfilename([]))
299
308
self.controlfile('README', 'w').write(
312
321
# if we want per-tree root ids then this is the place to set
313
322
# them; they're not needed for now and so ommitted for
315
f = self.controlfile('inventory','w')
316
bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
324
pack_xml(Inventory(), self.controlfile('inventory','w'))
319
326
def _check_format(self):
320
327
"""Check this branch format is supported.
328
335
# on Windows from Linux and so on. I think it might be better
329
336
# to always make all internal files in unix format.
330
337
fmt = self.controlfile('branch-format', 'r').read()
331
fmt = fmt.replace('\r\n', '\n')
338
fmt.replace('\r\n', '')
332
339
if fmt != BZR_BRANCH_FORMAT:
333
340
raise BzrError('sorry, branch format %r not supported' % fmt,
334
341
['use a different bzr version',
354
361
def read_working_inventory(self):
355
362
"""Read the working inventory."""
356
363
from bzrlib.inventory import Inventory
364
from bzrlib.xml import unpack_xml
365
from time import time
359
369
# ElementTree does its own conversion from UTF-8, so open in
361
f = self.controlfile('inventory', 'rb')
362
return bzrlib.xml.serializer_v4.read_inventory(f)
371
inv = unpack_xml(Inventory,
372
self.controlfile('inventory', 'rb'))
373
mutter("loaded inventory of %d items in %f"
374
% (len(inv), time() - before))
371
384
will be committed to the next revision.
373
386
from bzrlib.atomicfile import AtomicFile
387
from bzrlib.xml import pack_xml
375
389
self.lock_write()
377
391
f = AtomicFile(self.controlfilename('inventory'), 'wb')
379
bzrlib.xml.serializer_v4.write_inventory(inv, f)
570
def get_revision_xml_file(self, revision_id):
584
def get_revision_xml(self, revision_id):
571
585
"""Return XML file object for revision object."""
572
586
if not revision_id or not isinstance(revision_id, basestring):
573
587
raise InvalidRevisionId(revision_id)
578
592
return self.revision_store[revision_id]
579
except (IndexError, KeyError):
580
594
raise bzrlib.errors.NoSuchRevision(self, revision_id)
586
get_revision_xml = get_revision_xml_file
589
599
def get_revision(self, revision_id):
590
600
"""Return the Revision object for a named revision"""
591
xml_file = self.get_revision_xml_file(revision_id)
601
xml_file = self.get_revision_xml(revision_id)
594
r = bzrlib.xml.serializer_v4.read_revision(xml_file)
604
r = unpack_xml(Revision, xml_file)
595
605
except SyntaxError, e:
596
606
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
642
652
parameter which can be either an integer revno or a
644
654
from bzrlib.inventory import Inventory
655
from bzrlib.xml import unpack_xml
646
f = self.get_inventory_xml_file(inventory_id)
647
return bzrlib.xml.serializer_v4.read_inventory(f)
657
return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
650
660
def get_inventory_xml(self, inventory_id):
651
661
"""Get inventory XML as a file object."""
652
662
return self.inventory_store[inventory_id]
654
get_inventory_xml_file = get_inventory_xml
657
665
def get_inventory_sha1(self, inventory_id):
688
696
def common_ancestor(self, other, self_revno=None, other_revno=None):
690
>>> from bzrlib.commit import commit
691
699
>>> sb = ScratchBranch(files=['foo', 'foo~'])
692
700
>>> sb.common_ancestor(sb) == (None, None)
694
>>> commit(sb, "Committing first revision", verbose=False)
702
>>> commit.commit(sb, "Committing first revision", verbose=False)
695
703
>>> sb.common_ancestor(sb)[0]
697
705
>>> clone = sb.clone()
698
>>> commit(sb, "Committing second revision", verbose=False)
706
>>> commit.commit(sb, "Committing second revision", verbose=False)
699
707
>>> sb.common_ancestor(sb)[0]
701
709
>>> sb.common_ancestor(clone)[0]
703
>>> commit(clone, "Committing divergent second revision",
711
>>> commit.commit(clone, "Committing divergent second revision",
704
712
... verbose=False)
705
713
>>> sb.common_ancestor(clone)[0]
797
805
"""Pull in all new revisions from other branch.
799
807
from bzrlib.fetch import greedy_fetch
800
from bzrlib.revision import get_intervening_revisions
802
809
pb = bzrlib.ui.ui_factory.progress_bar()
803
810
pb.update('comparing histories')
804
if stop_revision is None:
805
other_revision = other.last_patch()
812
revision_ids = self.missing_revisions(other, stop_revision)
814
if len(revision_ids) > 0:
815
count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
807
other_revision = other.lookup_revision(stop_revision)
808
count = greedy_fetch(self, other, other_revision, pb)[0]
810
revision_ids = self.missing_revisions(other, stop_revision)
811
except DivergedBranches, e:
813
revision_ids = get_intervening_revisions(self.last_patch(),
814
other_revision, self)
815
assert self.last_patch() not in revision_ids
816
except bzrlib.errors.NotAncestor:
819
818
self.append_revision(*revision_ids)
819
## note("Added %d revisions." % count)
822
822
def install_revisions(self, other, revision_ids, pb):
823
823
if hasattr(other.revision_store, "prefetch"):
824
824
other.revision_store.prefetch(revision_ids)
825
825
if hasattr(other.inventory_store, "prefetch"):
827
for rev_id in revision_ids:
829
revision = other.get_revision(rev_id).inventory_id
830
inventory_ids.append(revision)
831
except bzrlib.errors.NoSuchRevision:
826
inventory_ids = [other.get_revision(r).inventory_id
827
for r in revision_ids]
833
828
other.inventory_store.prefetch(inventory_ids)
1084
1079
REVISION_NAMESPACES['date:'] = _namespace_date
1087
def _namespace_ancestor(self, revs, revision):
1088
from revision import common_ancestor, MultipleRevisionSources
1089
other_branch = find_branch(_trim_namespace('ancestor', revision))
1090
revision_a = self.last_patch()
1091
revision_b = other_branch.last_patch()
1092
for r, b in ((revision_a, self), (revision_b, other_branch)):
1094
raise bzrlib.errors.NoCommits(b)
1095
revision_source = MultipleRevisionSources(self, other_branch)
1096
result = common_ancestor(revision_a, revision_b, revision_source)
1098
revno = self.revision_id_to_revno(result)
1099
except bzrlib.errors.NoSuchRevision:
1104
REVISION_NAMESPACES['ancestor:'] = _namespace_ancestor
1106
1081
def revision_tree(self, revision_id):
1107
1082
"""Return Tree for a revision on this branch.
1120
1095
def working_tree(self):
1121
1096
"""Return a `Tree` for the working copy."""
1122
from bzrlib.workingtree import WorkingTree
1097
from workingtree import WorkingTree
1123
1098
return WorkingTree(self.base, self.read_working_inventory())
1376
def check_revno(self, revno):
1378
Check whether a revno corresponds to any revision.
1379
Zero (the NULL revision) is considered valid.
1382
self.check_real_revno(revno)
1384
def check_real_revno(self, revno):
1386
Check whether a revno corresponds to a real revision.
1387
Zero (the NULL revision) is considered invalid
1389
if revno < 1 or revno > self.revno():
1390
raise InvalidRevisionNumber(revno)
1516
1475
return gen_file_id('TREE_ROOT')
1478
def pull_loc(branch):
1479
# TODO: Should perhaps just make attribute be 'base' in
1480
# RemoteBranch and Branch?
1481
if hasattr(branch, "baseurl"):
1482
return branch.baseurl
1519
1487
def copy_branch(branch_from, to_location, revision=None):
1520
1488
"""Copy branch_from into the existing directory to_location.
1527
1495
The name of a local directory that exists but is empty.
1529
1497
from bzrlib.merge import merge
1498
from bzrlib.branch import Branch
1531
1500
assert isinstance(branch_from, Branch)
1532
1501
assert isinstance(to_location, basestring)
1540
1509
br_to.update_revisions(branch_from, stop_revision=revno)
1541
1510
merge((to_location, -1), (to_location, 0), this_dir=to_location,
1542
1511
check_clean=False, ignore_zero=True)
1543
br_to.set_parent(branch_from.base)
1513
from_location = pull_loc(branch_from)
1514
br_to.set_parent(pull_loc(branch_from))
1546
def _trim_namespace(namespace, spec):
1547
full_namespace = namespace + ':'
1548
assert spec.startswith(full_namespace)
1549
return spec[len(full_namespace):]