~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Aaron Bentley
  • Date: 2005-08-25 13:10:25 UTC
  • mfrom: (974.1.38)
  • mto: (1092.1.42) (1185.3.4)
  • mto: This revision was merged to the branch mainline in revision 1178.
  • Revision ID: abentley@panoramicfeedback.com-20050825131025-2aa94bcbbd646a00
Fixed return value when not an ImmutableStore

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
from bzrlib.progress import ProgressBar
 
34
 
 
35
       
38
36
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
39
37
## TODO: Maybe include checks for common corruption of newlines, etc?
40
38
 
108
106
    It is not necessary that f exists.
109
107
 
110
108
    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
 
    """
 
109
    run into the root."""
113
110
    if f == None:
114
111
        f = os.getcwd()
115
112
    elif hasattr(os.path, 'realpath'):
128
125
        head, tail = os.path.split(f)
129
126
        if head == f:
130
127
            # reached the root, whatever that may be
131
 
            raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
 
128
            raise BzrError('%r is not in a branch' % orig_f)
132
129
        f = head
133
 
 
134
 
 
135
 
 
136
 
# XXX: move into bzrlib.errors; subclass BzrError    
 
130
    
137
131
class DivergedBranches(Exception):
138
132
    def __init__(self, branch1, branch2):
139
133
        self.branch1 = branch1
219
213
            self._lock.unlock()
220
214
 
221
215
 
 
216
 
222
217
    def lock_write(self):
223
218
        if self._lock_mode:
224
219
            if self._lock_mode != 'w':
234
229
            self._lock_count = 1
235
230
 
236
231
 
 
232
 
237
233
    def lock_read(self):
238
234
        if self._lock_mode:
239
235
            assert self._lock_mode in ('r', 'w'), \
246
242
            self._lock_mode = 'r'
247
243
            self._lock_count = 1
248
244
                        
 
245
 
 
246
            
249
247
    def unlock(self):
250
248
        if not self._lock_mode:
251
249
            from errors import LockError
258
256
            self._lock = None
259
257
            self._lock_mode = self._lock_count = None
260
258
 
 
259
 
261
260
    def abspath(self, name):
262
261
        """Return absolute filename for something in the branch"""
263
262
        return os.path.join(self.base, name)
264
263
 
 
264
 
265
265
    def relpath(self, path):
266
266
        """Return path relative to this branch of something inside it.
267
267
 
268
268
        Raises an error if path is not in this branch."""
269
269
        return _relpath(self.base, path)
270
270
 
 
271
 
271
272
    def controlfilename(self, file_or_path):
272
273
        """Return location relative to branch."""
273
274
        if isinstance(file_or_path, basestring):
300
301
        else:
301
302
            raise BzrError("invalid controlfile mode %r" % mode)
302
303
 
 
304
 
 
305
 
303
306
    def _make_control(self):
304
307
        from bzrlib.inventory import Inventory
305
308
        from bzrlib.xml import pack_xml
323
326
        # simplicity.
324
327
        pack_xml(Inventory(), self.controlfile('inventory','w'))
325
328
 
 
329
 
326
330
    def _check_format(self):
327
331
        """Check this branch format is supported.
328
332
 
803
807
        if stop_revision is None:
804
808
            stop_revision = other_len
805
809
        elif stop_revision > other_len:
806
 
            raise bzrlib.errors.NoSuchRevision(self, stop_revision)
 
810
            raise NoSuchRevision(self, stop_revision)
807
811
        
808
812
        return other_history[self_len:stop_revision]
809
813
 
810
814
 
811
815
    def update_revisions(self, other, stop_revision=None):
812
816
        """Pull in all new revisions from other branch.
 
817
        
 
818
        >>> from bzrlib.commit import commit
 
819
        >>> bzrlib.trace.silent = True
 
820
        >>> br1 = ScratchBranch(files=['foo', 'bar'])
 
821
        >>> br1.add('foo')
 
822
        >>> br1.add('bar')
 
823
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
 
824
        >>> br2 = ScratchBranch()
 
825
        >>> br2.update_revisions(br1)
 
826
        Added 2 texts.
 
827
        Added 1 inventories.
 
828
        Added 1 revisions.
 
829
        >>> br2.revision_history()
 
830
        [u'REVISION-ID-1']
 
831
        >>> br2.update_revisions(br1)
 
832
        Added 0 revisions.
 
833
        >>> br1.text_store.total_size() == br2.text_store.total_size()
 
834
        True
813
835
        """
814
836
        from bzrlib.fetch import greedy_fetch
815
 
 
816
 
        pb = bzrlib.ui.ui_factory.progress_bar()
 
837
        pb = ProgressBar()
817
838
        pb.update('comparing histories')
818
 
 
819
839
        revision_ids = self.missing_revisions(other, stop_revision)
820
 
 
821
840
        if len(revision_ids) > 0:
822
841
            count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
823
842
        else:
824
843
            count = 0
825
844
        self.append_revision(*revision_ids)
826
 
        ## note("Added %d revisions." % count)
827
 
        pb.clear()
828
 
 
829
 
    def install_revisions(self, other, revision_ids, pb):
 
845
        print "Added %d revisions." % count
 
846
                    
 
847
    def install_revisions(self, other, revision_ids, pb=None):
 
848
        if pb is None:
 
849
            pb = ProgressBar()
830
850
        if hasattr(other.revision_store, "prefetch"):
831
851
            other.revision_store.prefetch(revision_ids)
832
852
        if hasattr(other.inventory_store, "prefetch"):
833
853
            inventory_ids = [other.get_revision(r).inventory_id
834
854
                             for r in revision_ids]
835
855
            other.inventory_store.prefetch(inventory_ids)
836
 
 
837
 
        if pb is None:
838
 
            pb = bzrlib.ui.ui_factory.progress_bar()
839
856
                
840
857
        revisions = []
841
858
        needed_texts = set()
842
859
        i = 0
843
 
 
844
860
        failures = set()
845
861
        for i, rev_id in enumerate(revision_ids):
846
862
            pb.update('fetching revision', i+1, len(revision_ids))
849
865
            except bzrlib.errors.NoSuchRevision:
850
866
                failures.add(rev_id)
851
867
                continue
852
 
 
853
868
            revisions.append(rev)
854
869
            inv = other.get_inventory(str(rev.inventory_id))
855
870
            for key, entry in inv.iter_entries():
862
877
                    
863
878
        count, cp_fail = self.text_store.copy_multi(other.text_store, 
864
879
                                                    needed_texts)
865
 
        #print "Added %d texts." % count 
 
880
        print "Added %d texts." % count 
866
881
        inventory_ids = [ f.inventory_id for f in revisions ]
867
882
        count, cp_fail = self.inventory_store.copy_multi(other.inventory_store, 
868
883
                                                         inventory_ids)
869
 
        #print "Added %d inventories." % count 
 
884
        print "Added %d inventories." % count 
870
885
        revision_ids = [ f.revision_id for f in revisions]
871
 
 
872
886
        count, cp_fail = self.revision_store.copy_multi(other.revision_store, 
873
887
                                                          revision_ids,
874
888
                                                          permit_failure=True)
875
889
        assert len(cp_fail) == 0 
876
890
        return count, failures
877
891
       
878
 
 
879
892
    def commit(self, *args, **kw):
880
893
        from bzrlib.commit import commit
881
894
        commit(self, *args, **kw)
883
896
 
884
897
    def lookup_revision(self, revision):
885
898
        """Return the revision identifier for a given revision information."""
886
 
        revno, info = self._get_revision_info(revision)
 
899
        revno, info = self.get_revision_info(revision)
887
900
        return info
888
901
 
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
902
    def get_revision_info(self, revision):
900
903
        """Return (revno, revision id) for revision identifier.
901
904
 
904
907
        revision can also be a string, in which case it is parsed for something like
905
908
            'date:' or 'revid:' etc.
906
909
        """
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
910
        if revision is None:
936
911
            return 0, None
937
912
        revno = None
941
916
            pass
942
917
        revs = self.revision_history()
943
918
        if isinstance(revision, int):
 
919
            if revision == 0:
 
920
                return 0, None
 
921
            # Mabye we should do this first, but we don't need it if revision == 0
944
922
            if revision < 0:
945
923
                revno = len(revs) + revision + 1
946
924
            else:
947
925
                revno = revision
948
 
            rev_id = self.get_rev_id(revno, revs)
949
926
        elif isinstance(revision, basestring):
950
927
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
951
928
                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)
 
929
                    revno = func(self, revs, revision)
958
930
                    break
959
931
            else:
960
 
                raise BzrError('No namespace registered for string: %r' %
961
 
                               revision)
962
 
        else:
963
 
            raise TypeError('Unhandled revision type %s' % revision)
 
932
                raise BzrError('No namespace registered for string: %r' % revision)
964
933
 
965
 
        if revno is None:
966
 
            if rev_id is None:
967
 
                raise bzrlib.errors.NoSuchRevision(self, revision)
968
 
        return revno, rev_id
 
934
        if revno is None or revno <= 0 or revno > len(revs):
 
935
            raise BzrError("no such revision %s" % revision)
 
936
        return revno, revs[revno-1]
969
937
 
970
938
    def _namespace_revno(self, revs, revision):
971
939
        """Lookup a revision by revision number"""
972
940
        assert revision.startswith('revno:')
973
941
        try:
974
 
            return (int(revision[6:]),)
 
942
            return int(revision[6:])
975
943
        except ValueError:
976
944
            return None
977
945
    REVISION_NAMESPACES['revno:'] = _namespace_revno
978
946
 
979
947
    def _namespace_revid(self, revs, revision):
980
948
        assert revision.startswith('revid:')
981
 
        rev_id = revision[len('revid:'):]
982
949
        try:
983
 
            return revs.index(rev_id) + 1, rev_id
 
950
            return revs.index(revision[6:]) + 1
984
951
        except ValueError:
985
 
            return None, rev_id
 
952
            return None
986
953
    REVISION_NAMESPACES['revid:'] = _namespace_revid
987
954
 
988
955
    def _namespace_last(self, revs, revision):
990
957
        try:
991
958
            offset = int(revision[5:])
992
959
        except ValueError:
993
 
            return (None,)
 
960
            return None
994
961
        else:
995
962
            if offset <= 0:
996
963
                raise BzrError('You must supply a positive value for --revision last:XXX')
997
 
            return (len(revs) - offset + 1,)
 
964
            return len(revs) - offset + 1
998
965
    REVISION_NAMESPACES['last:'] = _namespace_last
999
966
 
1000
967
    def _namespace_tag(self, revs, revision):
1075
1042
                # TODO: Handle timezone.
1076
1043
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1077
1044
                if first >= dt and (last is None or dt >= last):
1078
 
                    return (i+1,)
 
1045
                    return i+1
1079
1046
        else:
1080
1047
            for i in range(len(revs)):
1081
1048
                r = self.get_revision(revs[i])
1082
1049
                # TODO: Handle timezone.
1083
1050
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1084
1051
                if first <= dt and (last is None or dt <= last):
1085
 
                    return (i+1,)
 
1052
                    return i+1
1086
1053
    REVISION_NAMESPACES['date:'] = _namespace_date
1087
1054
 
1088
1055
    def revision_tree(self, revision_id):
1441
1408
    """Return a new tree-root file id."""
1442
1409
    return gen_file_id('TREE_ROOT')
1443
1410
 
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