~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-08-22 16:31:16 UTC
  • Revision ID: mbp@sourcefrog.net-20050822163116-935a433f338e7d5b
- new shell-complete command to help zsh completion

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
import sys, os
 
18
import sys
 
19
import os
19
20
 
20
21
import bzrlib
21
22
from bzrlib.trace import mutter, note
22
 
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, splitpath, \
 
23
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
 
24
     splitpath, \
23
25
     sha_file, appendpath, file_kind
24
 
from bzrlib.errors import BzrError
25
 
 
 
26
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
 
27
import bzrlib.errors
 
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
 
33
        
26
34
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
27
35
## TODO: Maybe include checks for common corruption of newlines, etc?
28
36
 
29
37
 
 
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.
 
41
 
 
42
# TODO: please move the revision-string syntax stuff out of the branch
 
43
# object; it's clutter
 
44
 
30
45
 
31
46
def find_branch(f, **args):
32
47
    if f and (f.startswith('http://') or f.startswith('https://')):
89
104
    It is not necessary that f exists.
90
105
 
91
106
    Basically we keep looking up until we find the control directory or
92
 
    run into the root."""
 
107
    run into the root.  If there isn't one, raises NotBranchError.
 
108
    """
93
109
    if f == None:
94
110
        f = os.getcwd()
95
111
    elif hasattr(os.path, 'realpath'):
108
124
        head, tail = os.path.split(f)
109
125
        if head == f:
110
126
            # reached the root, whatever that may be
111
 
            raise BzrError('%r is not in a branch' % orig_f)
 
127
            raise bzrlib.errors.NotBranchError('%r is not in a branch' % orig_f)
112
128
        f = head
113
 
    
 
129
 
 
130
 
 
131
 
 
132
# XXX: move into bzrlib.errors; subclass BzrError    
114
133
class DivergedBranches(Exception):
115
134
    def __init__(self, branch1, branch2):
116
135
        self.branch1 = branch1
118
137
        Exception.__init__(self, "These branches have diverged.")
119
138
 
120
139
 
121
 
class NoSuchRevision(BzrError):
122
 
    def __init__(self, branch, revision):
123
 
        self.branch = branch
124
 
        self.revision = revision
125
 
        msg = "Branch %s has no revision %d" % (branch, revision)
126
 
        BzrError.__init__(self, msg)
127
 
 
128
 
 
129
140
######################################################################
130
141
# branch objects
131
142
 
312
323
            self.controlfile(f, 'w').write('')
313
324
        mutter('created control directory in ' + self.base)
314
325
 
315
 
        pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
 
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
 
328
        # simplicity.
 
329
        pack_xml(Inventory(), self.controlfile('inventory','w'))
316
330
 
317
331
 
318
332
    def _check_format(self):
422
436
              add all non-ignored children.  Perhaps do that in a
423
437
              higher-level method.
424
438
        """
425
 
        from bzrlib.textui import show_status
426
439
        # TODO: Re-adding a file that is removed in the working copy
427
440
        # should probably put it back with the previous ID.
428
441
        if isinstance(files, basestring):
501
514
        is the opposite of add.  Removing it is consistent with most
502
515
        other tools.  Maybe an option.
503
516
        """
504
 
        from bzrlib.textui import show_status
505
517
        ## TODO: Normalize names
506
518
        ## TODO: Remove nested loops; better scalability
507
519
        if isinstance(files, basestring):
582
594
            f.close()
583
595
 
584
596
 
 
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)
 
601
 
 
602
        self.lock_read()
 
603
        try:
 
604
            try:
 
605
                return self.revision_store[revision_id]
 
606
            except IndexError:
 
607
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
 
608
        finally:
 
609
            self.unlock()
 
610
 
 
611
 
585
612
    def get_revision(self, revision_id):
586
613
        """Return the Revision object for a named revision"""
587
 
        from bzrlib.revision import Revision
588
 
        from bzrlib.xml import unpack_xml
 
614
        xml_file = self.get_revision_xml(revision_id)
589
615
 
590
 
        self.lock_read()
591
616
        try:
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])
595
 
        finally:
596
 
            self.unlock()
 
617
            r = unpack_xml(Revision, xml_file)
 
618
        except SyntaxError, e:
 
619
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
 
620
                                         [revision_id,
 
621
                                          str(e)])
597
622
            
598
623
        assert r.revision_id == revision_id
599
624
        return r
 
625
 
 
626
 
 
627
    def get_revision_delta(self, revno):
 
628
        """Return the delta for one revision.
 
629
 
 
630
        The delta is relative to its mainline predecessor, or the
 
631
        empty tree for revision 1.
 
632
        """
 
633
        assert isinstance(revno, int)
 
634
        rh = self.revision_history()
 
635
        if not (1 <= revno <= len(rh)):
 
636
            raise InvalidRevisionNumber(revno)
 
637
 
 
638
        # revno is 1-based; list is 0-based
 
639
 
 
640
        new_tree = self.revision_tree(rh[revno-1])
 
641
        if revno == 1:
 
642
            old_tree = EmptyTree()
 
643
        else:
 
644
            old_tree = self.revision_tree(rh[revno-2])
 
645
 
 
646
        return compare_trees(old_tree, new_tree)
 
647
 
600
648
        
601
649
 
602
650
    def get_revision_sha1(self, revision_id):
607
655
        # the revision, (add signatures/remove signatures) and still
608
656
        # have all hash pointers stay consistent.
609
657
        # But for now, just hash the contents.
610
 
        return sha_file(self.revision_store[revision_id])
 
658
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
611
659
 
612
660
 
613
661
    def get_inventory(self, inventory_id):
619
667
        from bzrlib.inventory import Inventory
620
668
        from bzrlib.xml import unpack_xml
621
669
 
622
 
        return unpack_xml(Inventory, self.inventory_store[inventory_id])
 
670
        return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
 
671
 
 
672
 
 
673
    def get_inventory_xml(self, inventory_id):
 
674
        """Get inventory XML as a file object."""
 
675
        return self.inventory_store[inventory_id]
623
676
            
624
677
 
625
678
    def get_inventory_sha1(self, inventory_id):
626
679
        """Return the sha1 hash of the inventory entry
627
680
        """
628
 
        return sha_file(self.inventory_store[inventory_id])
 
681
        return sha_file(self.get_inventory_xml(inventory_id))
629
682
 
630
683
 
631
684
    def get_revision_inventory(self, revision_id):
697
750
                return r+1, my_history[r]
698
751
        return None, None
699
752
 
700
 
    def enum_history(self, direction):
701
 
        """Return (revno, revision_id) for history of branch.
702
 
 
703
 
        direction
704
 
            'forward' is from earliest to latest
705
 
            'reverse' is from latest to earliest
706
 
        """
707
 
        rh = self.revision_history()
708
 
        if direction == 'forward':
709
 
            i = 1
710
 
            for rid in rh:
711
 
                yield i, rid
712
 
                i += 1
713
 
        elif direction == 'reverse':
714
 
            i = len(rh)
715
 
            while i > 0:
716
 
                yield i, rh[i-1]
717
 
                i -= 1
718
 
        else:
719
 
            raise ValueError('invalid history direction', direction)
720
 
 
721
753
 
722
754
    def revno(self):
723
755
        """Return current revision number for this branch.
1018
1050
 
1019
1051
        `revision_id` may be None for the null revision, in which case
1020
1052
        an `EmptyTree` is returned."""
1021
 
        from bzrlib.tree import EmptyTree, RevisionTree
1022
1053
        # TODO: refactor this to use an existing revision object
1023
1054
        # so we don't need to read it in twice.
1024
1055
        if revision_id == None:
1025
 
            return EmptyTree(self.get_root_id())
 
1056
            return EmptyTree()
1026
1057
        else:
1027
1058
            inv = self.get_revision_inventory(revision_id)
1028
1059
            return RevisionTree(self.text_store, inv)
1039
1070
 
1040
1071
        If there are no revisions yet, return an `EmptyTree`.
1041
1072
        """
1042
 
        from bzrlib.tree import EmptyTree, RevisionTree
1043
1073
        r = self.last_patch()
1044
1074
        if r == None:
1045
 
            return EmptyTree(self.get_root_id())
 
1075
            return EmptyTree()
1046
1076
        else:
1047
1077
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
1048
1078