15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
21
from bzrlib.trace import mutter, note
23
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
22
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, splitpath, \
25
23
sha_file, appendpath, file_kind
26
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
28
from bzrlib.textui import show_status
29
from bzrlib.revision import Revision
30
from bzrlib.xml import unpack_xml
31
from bzrlib.delta import compare_trees
32
from bzrlib.tree import EmptyTree, RevisionTree
24
from bzrlib.errors import BzrError
34
26
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
35
27
## TODO: Maybe include checks for common corruption of newlines, etc?
38
# TODO: Some operations like log might retrieve the same revisions
39
# repeatedly to calculate deltas. We could perhaps have a weakref
40
# cache in memory to make this faster.
42
# TODO: please move the revision-string syntax stuff out of the branch
43
# object; it's clutter
46
31
def find_branch(f, **args):
47
32
if f and (f.startswith('http://') or f.startswith('https://')):
104
89
It is not necessary that f exists.
106
91
Basically we keep looking up until we find the control directory or
107
run into the root. If there isn't one, raises NotBranchError.
111
95
elif hasattr(os.path, 'realpath'):
124
108
head, tail = os.path.split(f)
126
110
# reached the root, whatever that may be
127
raise bzrlib.errors.NotBranchError('%r is not in a branch' % orig_f)
111
raise BzrError('%r is not in a branch' % orig_f)
132
# XXX: move into bzrlib.errors; subclass BzrError
133
114
class DivergedBranches(Exception):
134
115
def __init__(self, branch1, branch2):
135
116
self.branch1 = branch1
137
118
Exception.__init__(self, "These branches have diverged.")
121
class NoSuchRevision(BzrError):
122
def __init__(self, branch, revision):
124
self.revision = revision
125
msg = "Branch %s has no revision %d" % (branch, revision)
126
BzrError.__init__(self, msg)
140
129
######################################################################
323
312
self.controlfile(f, 'w').write('')
324
313
mutter('created control directory in ' + self.base)
326
# if we want per-tree root ids then this is the place to set
327
# them; they're not needed for now and so ommitted for
329
pack_xml(Inventory(), self.controlfile('inventory','w'))
315
pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
332
318
def _check_format(self):
375
361
# ElementTree does its own conversion from UTF-8, so open in
377
363
inv = unpack_xml(Inventory,
378
self.controlfile('inventory', 'rb'))
364
self.controlfile('inventory', 'rb'))
379
365
mutter("loaded inventory of %d items in %f"
380
366
% (len(inv), time() - before))
436
422
add all non-ignored children. Perhaps do that in a
437
423
higher-level method.
425
from bzrlib.textui import show_status
439
426
# TODO: Re-adding a file that is removed in the working copy
440
427
# should probably put it back with the previous ID.
441
428
if isinstance(files, basestring):
514
501
is the opposite of add. Removing it is consistent with most
515
502
other tools. Maybe an option.
504
from bzrlib.textui import show_status
517
505
## TODO: Normalize names
518
506
## TODO: Remove nested loops; better scalability
519
507
if isinstance(files, basestring):
597
def get_revision_xml(self, revision_id):
598
"""Return XML file object for revision object."""
599
if not revision_id or not isinstance(revision_id, basestring):
600
raise InvalidRevisionId(revision_id)
605
return self.revision_store[revision_id]
607
raise bzrlib.errors.NoSuchRevision(self, revision_id)
612
585
def get_revision(self, revision_id):
613
586
"""Return the Revision object for a named revision"""
614
xml_file = self.get_revision_xml(revision_id)
587
from bzrlib.revision import Revision
588
from bzrlib.xml import unpack_xml
617
r = unpack_xml(Revision, xml_file)
618
except SyntaxError, e:
619
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
592
if not revision_id or not isinstance(revision_id, basestring):
593
raise ValueError('invalid revision-id: %r' % revision_id)
594
r = unpack_xml(Revision, self.revision_store[revision_id])
623
598
assert r.revision_id == revision_id
627
def get_revision_delta(self, revno):
628
"""Return the delta for one revision.
630
The delta is relative to its mainline predecessor, or the
631
empty tree for revision 1.
633
assert isinstance(revno, int)
634
rh = self.revision_history()
635
if not (1 <= revno <= len(rh)):
636
raise InvalidRevisionNumber(revno)
638
# revno is 1-based; list is 0-based
640
new_tree = self.revision_tree(rh[revno-1])
642
old_tree = EmptyTree()
644
old_tree = self.revision_tree(rh[revno-2])
646
return compare_trees(old_tree, new_tree)
650
602
def get_revision_sha1(self, revision_id):
655
607
# the revision, (add signatures/remove signatures) and still
656
608
# have all hash pointers stay consistent.
657
609
# But for now, just hash the contents.
658
return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
610
return sha_file(self.revision_store[revision_id])
661
613
def get_inventory(self, inventory_id):
667
619
from bzrlib.inventory import Inventory
668
620
from bzrlib.xml import unpack_xml
670
return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
673
def get_inventory_xml(self, inventory_id):
674
"""Get inventory XML as a file object."""
675
return self.inventory_store[inventory_id]
622
return unpack_xml(Inventory, self.inventory_store[inventory_id])
678
625
def get_inventory_sha1(self, inventory_id):
679
626
"""Return the sha1 hash of the inventory entry
681
return sha_file(self.get_inventory_xml(inventory_id))
628
return sha_file(self.inventory_store[inventory_id])
684
631
def get_revision_inventory(self, revision_id):
750
697
return r+1, my_history[r]
751
698
return None, None
700
def enum_history(self, direction):
701
"""Return (revno, revision_id) for history of branch.
704
'forward' is from earliest to latest
705
'reverse' is from latest to earliest
707
rh = self.revision_history()
708
if direction == 'forward':
713
elif direction == 'reverse':
719
raise ValueError('invalid history direction', direction)
755
723
"""Return current revision number for this branch.
1051
1023
`revision_id` may be None for the null revision, in which case
1052
1024
an `EmptyTree` is returned."""
1025
from bzrlib.tree import EmptyTree, RevisionTree
1053
1026
# TODO: refactor this to use an existing revision object
1054
1027
# so we don't need to read it in twice.
1055
1028
if revision_id == None:
1029
return EmptyTree(self.get_root_id())
1058
1031
inv = self.get_revision_inventory(revision_id)
1059
1032
return RevisionTree(self.text_store, inv)
1071
1044
If there are no revisions yet, return an `EmptyTree`.
1046
from bzrlib.tree import EmptyTree, RevisionTree
1073
1047
r = self.last_patch()
1049
return EmptyTree(self.get_root_id())
1077
1051
return RevisionTree(self.text_store, self.get_revision_inventory(r))