~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-08-17 03:31:19 UTC
  • Revision ID: mbp@sourcefrog.net-20050817033119-1976931eac3199db
todo

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
24
24
     splitpath, \
25
25
     sha_file, appendpath, file_kind
26
 
 
27
26
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
28
27
import bzrlib.errors
29
28
from bzrlib.textui import show_status
31
30
from bzrlib.xml import unpack_xml
32
31
from bzrlib.delta import compare_trees
33
32
from bzrlib.tree import EmptyTree, RevisionTree
34
 
import bzrlib.ui
35
 
 
36
 
 
37
 
 
 
33
        
38
34
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
39
35
## TODO: Maybe include checks for common corruption of newlines, etc?
40
36
 
43
39
# repeatedly to calculate deltas.  We could perhaps have a weakref
44
40
# cache in memory to make this faster.
45
41
 
46
 
# TODO: please move the revision-string syntax stuff out of the branch
47
 
# object; it's clutter
48
 
 
49
42
 
50
43
def find_branch(f, **args):
51
44
    if f and (f.startswith('http://') or f.startswith('https://')):
108
101
    It is not necessary that f exists.
109
102
 
110
103
    Basically we keep looking up until we find the control directory or
111
 
    run into the root.  If there isn't one, raises NotBranchError.
112
 
    """
 
104
    run into the root."""
113
105
    if f == None:
114
106
        f = os.getcwd()
115
107
    elif hasattr(os.path, 'realpath'):
128
120
        head, tail = os.path.split(f)
129
121
        if head == f:
130
122
            # reached the root, whatever that may be
131
 
            raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
 
123
            raise BzrError('%r is not in a branch' % orig_f)
132
124
        f = head
133
 
 
134
 
 
135
 
 
136
 
# XXX: move into bzrlib.errors; subclass BzrError    
 
125
    
137
126
class DivergedBranches(Exception):
138
127
    def __init__(self, branch1, branch2):
139
128
        self.branch1 = branch1
219
208
            self._lock.unlock()
220
209
 
221
210
 
 
211
 
222
212
    def lock_write(self):
223
213
        if self._lock_mode:
224
214
            if self._lock_mode != 'w':
234
224
            self._lock_count = 1
235
225
 
236
226
 
 
227
 
237
228
    def lock_read(self):
238
229
        if self._lock_mode:
239
230
            assert self._lock_mode in ('r', 'w'), \
246
237
            self._lock_mode = 'r'
247
238
            self._lock_count = 1
248
239
                        
 
240
 
 
241
            
249
242
    def unlock(self):
250
243
        if not self._lock_mode:
251
244
            from errors import LockError
258
251
            self._lock = None
259
252
            self._lock_mode = self._lock_count = None
260
253
 
 
254
 
261
255
    def abspath(self, name):
262
256
        """Return absolute filename for something in the branch"""
263
257
        return os.path.join(self.base, name)
264
258
 
 
259
 
265
260
    def relpath(self, path):
266
261
        """Return path relative to this branch of something inside it.
267
262
 
268
263
        Raises an error if path is not in this branch."""
269
264
        return _relpath(self.base, path)
270
265
 
 
266
 
271
267
    def controlfilename(self, file_or_path):
272
268
        """Return location relative to branch."""
273
269
        if isinstance(file_or_path, basestring):
300
296
        else:
301
297
            raise BzrError("invalid controlfile mode %r" % mode)
302
298
 
 
299
 
 
300
 
303
301
    def _make_control(self):
304
302
        from bzrlib.inventory import Inventory
305
303
        from bzrlib.xml import pack_xml
323
321
        # simplicity.
324
322
        pack_xml(Inventory(), self.controlfile('inventory','w'))
325
323
 
 
324
 
326
325
    def _check_format(self):
327
326
        """Check this branch format is supported.
328
327
 
764
763
            return None
765
764
 
766
765
 
767
 
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
 
766
    def missing_revisions(self, other, stop_revision=None):
768
767
        """
769
768
        If self and other have not diverged, return a list of the revisions
770
769
        present in other, but missing from self.
803
802
        if stop_revision is None:
804
803
            stop_revision = other_len
805
804
        elif stop_revision > other_len:
806
 
            raise bzrlib.errors.NoSuchRevision(self, stop_revision)
 
805
            raise NoSuchRevision(self, stop_revision)
807
806
        
808
807
        return other_history[self_len:stop_revision]
809
808
 
810
809
 
811
810
    def update_revisions(self, other, stop_revision=None):
812
811
        """Pull in all new revisions from other branch.
 
812
        
 
813
        >>> from bzrlib.commit import commit
 
814
        >>> bzrlib.trace.silent = True
 
815
        >>> br1 = ScratchBranch(files=['foo', 'bar'])
 
816
        >>> br1.add('foo')
 
817
        >>> br1.add('bar')
 
818
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
 
819
        >>> br2 = ScratchBranch()
 
820
        >>> br2.update_revisions(br1)
 
821
        Added 2 texts.
 
822
        Added 1 inventories.
 
823
        Added 1 revisions.
 
824
        >>> br2.revision_history()
 
825
        [u'REVISION-ID-1']
 
826
        >>> br2.update_revisions(br1)
 
827
        Added 0 texts.
 
828
        Added 0 inventories.
 
829
        Added 0 revisions.
 
830
        >>> br1.text_store.total_size() == br2.text_store.total_size()
 
831
        True
813
832
        """
814
 
        from bzrlib.fetch import greedy_fetch
815
 
 
816
 
        pb = bzrlib.ui.ui_factory.progress_bar()
 
833
        from bzrlib.progress import ProgressBar
 
834
 
 
835
        pb = ProgressBar()
 
836
 
817
837
        pb.update('comparing histories')
818
 
 
819
838
        revision_ids = self.missing_revisions(other, stop_revision)
820
839
 
821
 
        if len(revision_ids) > 0:
822
 
            count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
823
 
        else:
824
 
            count = 0
825
 
        self.append_revision(*revision_ids)
826
 
        ## note("Added %d revisions." % count)
827
 
        pb.clear()
828
 
 
829
 
    def install_revisions(self, other, revision_ids, pb):
830
840
        if hasattr(other.revision_store, "prefetch"):
831
841
            other.revision_store.prefetch(revision_ids)
832
842
        if hasattr(other.inventory_store, "prefetch"):
833
843
            inventory_ids = [other.get_revision(r).inventory_id
834
844
                             for r in revision_ids]
835
845
            other.inventory_store.prefetch(inventory_ids)
836
 
 
837
 
        if pb is None:
838
 
            pb = bzrlib.ui.ui_factory.progress_bar()
839
846
                
840
847
        revisions = []
841
848
        needed_texts = set()
842
849
        i = 0
843
 
 
844
 
        failures = set()
845
 
        for i, rev_id in enumerate(revision_ids):
846
 
            pb.update('fetching revision', i+1, len(revision_ids))
847
 
            try:
848
 
                rev = other.get_revision(rev_id)
849
 
            except bzrlib.errors.NoSuchRevision:
850
 
                failures.add(rev_id)
851
 
                continue
852
 
 
 
850
        for rev_id in revision_ids:
 
851
            i += 1
 
852
            pb.update('fetching revision', i, len(revision_ids))
 
853
            rev = other.get_revision(rev_id)
853
854
            revisions.append(rev)
854
855
            inv = other.get_inventory(str(rev.inventory_id))
855
856
            for key, entry in inv.iter_entries():
860
861
 
861
862
        pb.clear()
862
863
                    
863
 
        count, cp_fail = self.text_store.copy_multi(other.text_store, 
864
 
                                                    needed_texts)
865
 
        #print "Added %d texts." % count 
 
864
        count = self.text_store.copy_multi(other.text_store, needed_texts)
 
865
        print "Added %d texts." % count 
866
866
        inventory_ids = [ f.inventory_id for f in revisions ]
867
 
        count, cp_fail = self.inventory_store.copy_multi(other.inventory_store, 
868
 
                                                         inventory_ids)
869
 
        #print "Added %d inventories." % count 
 
867
        count = self.inventory_store.copy_multi(other.inventory_store, 
 
868
                                                inventory_ids)
 
869
        print "Added %d inventories." % count 
870
870
        revision_ids = [ f.revision_id for f in revisions]
871
 
 
872
 
        count, cp_fail = self.revision_store.copy_multi(other.revision_store, 
873
 
                                                          revision_ids,
874
 
                                                          permit_failure=True)
875
 
        assert len(cp_fail) == 0 
876
 
        return count, failures
877
 
       
878
 
 
 
871
        count = self.revision_store.copy_multi(other.revision_store, 
 
872
                                               revision_ids)
 
873
        for revision_id in revision_ids:
 
874
            self.append_revision(revision_id)
 
875
        print "Added %d revisions." % count
 
876
                    
 
877
        
879
878
    def commit(self, *args, **kw):
880
879
        from bzrlib.commit import commit
881
880
        commit(self, *args, **kw)
883
882
 
884
883
    def lookup_revision(self, revision):
885
884
        """Return the revision identifier for a given revision information."""
886
 
        revno, info = self._get_revision_info(revision)
 
885
        revno, info = self.get_revision_info(revision)
887
886
        return info
888
887
 
889
 
 
890
 
    def revision_id_to_revno(self, revision_id):
891
 
        """Given a revision id, return its revno"""
892
 
        history = self.revision_history()
893
 
        try:
894
 
            return history.index(revision_id) + 1
895
 
        except ValueError:
896
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
897
 
 
898
 
 
899
888
    def get_revision_info(self, revision):
900
889
        """Return (revno, revision id) for revision identifier.
901
890
 
904
893
        revision can also be a string, in which case it is parsed for something like
905
894
            'date:' or 'revid:' etc.
906
895
        """
907
 
        revno, rev_id = self._get_revision_info(revision)
908
 
        if revno is None:
909
 
            raise bzrlib.errors.NoSuchRevision(self, revision)
910
 
        return revno, rev_id
911
 
 
912
 
    def get_rev_id(self, revno, history=None):
913
 
        """Find the revision id of the specified revno."""
914
 
        if revno == 0:
915
 
            return None
916
 
        if history is None:
917
 
            history = self.revision_history()
918
 
        elif revno <= 0 or revno > len(history):
919
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
920
 
        return history[revno - 1]
921
 
 
922
 
    def _get_revision_info(self, revision):
923
 
        """Return (revno, revision id) for revision specifier.
924
 
 
925
 
        revision can be an integer, in which case it is assumed to be revno
926
 
        (though this will translate negative values into positive ones)
927
 
        revision can also be a string, in which case it is parsed for something
928
 
        like 'date:' or 'revid:' etc.
929
 
 
930
 
        A revid is always returned.  If it is None, the specifier referred to
931
 
        the null revision.  If the revid does not occur in the revision
932
 
        history, revno will be None.
933
 
        """
934
 
        
935
896
        if revision is None:
936
897
            return 0, None
937
898
        revno = None
941
902
            pass
942
903
        revs = self.revision_history()
943
904
        if isinstance(revision, int):
 
905
            if revision == 0:
 
906
                return 0, None
 
907
            # Mabye we should do this first, but we don't need it if revision == 0
944
908
            if revision < 0:
945
909
                revno = len(revs) + revision + 1
946
910
            else:
947
911
                revno = revision
948
 
            rev_id = self.get_rev_id(revno, revs)
949
912
        elif isinstance(revision, basestring):
950
913
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
951
914
                if revision.startswith(prefix):
952
 
                    result = func(self, revs, revision)
953
 
                    if len(result) > 1:
954
 
                        revno, rev_id = result
955
 
                    else:
956
 
                        revno = result[0]
957
 
                        rev_id = self.get_rev_id(revno, revs)
 
915
                    revno = func(self, revs, revision)
958
916
                    break
959
917
            else:
960
 
                raise BzrError('No namespace registered for string: %r' %
961
 
                               revision)
962
 
        else:
963
 
            raise TypeError('Unhandled revision type %s' % revision)
 
918
                raise BzrError('No namespace registered for string: %r' % revision)
964
919
 
965
 
        if revno is None:
966
 
            if rev_id is None:
967
 
                raise bzrlib.errors.NoSuchRevision(self, revision)
968
 
        return revno, rev_id
 
920
        if revno is None or revno <= 0 or revno > len(revs):
 
921
            raise BzrError("no such revision %s" % revision)
 
922
        return revno, revs[revno-1]
969
923
 
970
924
    def _namespace_revno(self, revs, revision):
971
925
        """Lookup a revision by revision number"""
972
926
        assert revision.startswith('revno:')
973
927
        try:
974
 
            return (int(revision[6:]),)
 
928
            return int(revision[6:])
975
929
        except ValueError:
976
930
            return None
977
931
    REVISION_NAMESPACES['revno:'] = _namespace_revno
978
932
 
979
933
    def _namespace_revid(self, revs, revision):
980
934
        assert revision.startswith('revid:')
981
 
        rev_id = revision[len('revid:'):]
982
935
        try:
983
 
            return revs.index(rev_id) + 1, rev_id
 
936
            return revs.index(revision[6:]) + 1
984
937
        except ValueError:
985
 
            return None, rev_id
 
938
            return None
986
939
    REVISION_NAMESPACES['revid:'] = _namespace_revid
987
940
 
988
941
    def _namespace_last(self, revs, revision):
990
943
        try:
991
944
            offset = int(revision[5:])
992
945
        except ValueError:
993
 
            return (None,)
 
946
            return None
994
947
        else:
995
948
            if offset <= 0:
996
949
                raise BzrError('You must supply a positive value for --revision last:XXX')
997
 
            return (len(revs) - offset + 1,)
 
950
            return len(revs) - offset + 1
998
951
    REVISION_NAMESPACES['last:'] = _namespace_last
999
952
 
1000
953
    def _namespace_tag(self, revs, revision):
1075
1028
                # TODO: Handle timezone.
1076
1029
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1077
1030
                if first >= dt and (last is None or dt >= last):
1078
 
                    return (i+1,)
 
1031
                    return i+1
1079
1032
        else:
1080
1033
            for i in range(len(revs)):
1081
1034
                r = self.get_revision(revs[i])
1082
1035
                # TODO: Handle timezone.
1083
1036
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1084
1037
                if first <= dt and (last is None or dt <= last):
1085
 
                    return (i+1,)
 
1038
                    return i+1
1086
1039
    REVISION_NAMESPACES['date:'] = _namespace_date
1087
1040
 
1088
1041
    def revision_tree(self, revision_id):
1441
1394
    """Return a new tree-root file id."""
1442
1395
    return gen_file_id('TREE_ROOT')
1443
1396
 
1444
 
 
1445
 
def pull_loc(branch):
1446
 
    # TODO: Should perhaps just make attribute be 'base' in
1447
 
    # RemoteBranch and Branch?
1448
 
    if hasattr(branch, "baseurl"):
1449
 
        return branch.baseurl
1450
 
    else:
1451
 
        return branch.base
1452
 
 
1453
 
 
1454
 
def copy_branch(branch_from, to_location, revision=None):
1455
 
    """Copy branch_from into the existing directory to_location.
1456
 
 
1457
 
    If revision is not None, the head of the new branch will be revision.
1458
 
    """
1459
 
    from bzrlib.merge import merge
1460
 
    from bzrlib.branch import Branch
1461
 
    br_to = Branch(to_location, init=True)
1462
 
    br_to.set_root_id(branch_from.get_root_id())
1463
 
    if revision is None:
1464
 
        revno = branch_from.revno()
1465
 
    else:
1466
 
        revno, rev_id = branch_from.get_revision_info(revision)
1467
 
    br_to.update_revisions(branch_from, stop_revision=revno)
1468
 
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1469
 
          check_clean=False, ignore_zero=True)
1470
 
    from_location = pull_loc(branch_from)
1471
 
    br_to.controlfile("x-pull", "wb").write(from_location + "\n")
1472