~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

- fix _find_remote_branch to avoid strange error for nonexistent branch

  It used to keep recursing up until the url was invalid, giving an
  IDNA error.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
import bzrlib.errors
29
29
from bzrlib.textui import show_status
30
30
from bzrlib.revision import Revision
31
 
from bzrlib.xml import unpack_xml
32
31
from bzrlib.delta import compare_trees
33
32
from bzrlib.tree import EmptyTree, RevisionTree
 
33
import bzrlib.xml
34
34
import bzrlib.ui
35
35
 
36
36
 
302
302
 
303
303
    def _make_control(self):
304
304
        from bzrlib.inventory import Inventory
305
 
        from bzrlib.xml import pack_xml
306
305
        
307
306
        os.mkdir(self.controlfilename([]))
308
307
        self.controlfile('README', 'w').write(
321
320
        # if we want per-tree root ids then this is the place to set
322
321
        # them; they're not needed for now and so ommitted for
323
322
        # simplicity.
324
 
        pack_xml(Inventory(), self.controlfile('inventory','w'))
 
323
        f = self.controlfile('inventory','w')
 
324
        bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
 
325
 
325
326
 
326
327
    def _check_format(self):
327
328
        """Check this branch format is supported.
335
336
        # on Windows from Linux and so on.  I think it might be better
336
337
        # to always make all internal files in unix format.
337
338
        fmt = self.controlfile('branch-format', 'r').read()
338
 
        fmt.replace('\r\n', '')
 
339
        fmt = fmt.replace('\r\n', '\n')
339
340
        if fmt != BZR_BRANCH_FORMAT:
340
341
            raise BzrError('sorry, branch format %r not supported' % fmt,
341
342
                           ['use a different bzr version',
361
362
    def read_working_inventory(self):
362
363
        """Read the working inventory."""
363
364
        from bzrlib.inventory import Inventory
364
 
        from bzrlib.xml import unpack_xml
365
 
        from time import time
366
 
        before = time()
367
365
        self.lock_read()
368
366
        try:
369
367
            # ElementTree does its own conversion from UTF-8, so open in
370
368
            # binary.
371
 
            inv = unpack_xml(Inventory,
372
 
                             self.controlfile('inventory', 'rb'))
373
 
            mutter("loaded inventory of %d items in %f"
374
 
                   % (len(inv), time() - before))
375
 
            return inv
 
369
            f = self.controlfile('inventory', 'rb')
 
370
            return bzrlib.xml.serializer_v4.read_inventory(f)
376
371
        finally:
377
372
            self.unlock()
378
373
            
384
379
        will be committed to the next revision.
385
380
        """
386
381
        from bzrlib.atomicfile import AtomicFile
387
 
        from bzrlib.xml import pack_xml
388
382
        
389
383
        self.lock_write()
390
384
        try:
391
385
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
392
386
            try:
393
 
                pack_xml(inv, f)
 
387
                bzrlib.xml.serializer_v4.write_inventory(inv, f)
394
388
                f.commit()
395
389
            finally:
396
390
                f.close()
581
575
            f.close()
582
576
 
583
577
 
584
 
    def get_revision_xml(self, revision_id):
 
578
    def get_revision_xml_file(self, revision_id):
585
579
        """Return XML file object for revision object."""
586
580
        if not revision_id or not isinstance(revision_id, basestring):
587
581
            raise InvalidRevisionId(revision_id)
596
590
            self.unlock()
597
591
 
598
592
 
 
593
    #deprecated
 
594
    get_revision_xml = get_revision_xml_file
 
595
 
 
596
 
599
597
    def get_revision(self, revision_id):
600
598
        """Return the Revision object for a named revision"""
601
 
        xml_file = self.get_revision_xml(revision_id)
 
599
        xml_file = self.get_revision_xml_file(revision_id)
602
600
 
603
601
        try:
604
 
            r = unpack_xml(Revision, xml_file)
 
602
            r = bzrlib.xml.serializer_v4.read_revision(xml_file)
605
603
        except SyntaxError, e:
606
604
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
607
605
                                         [revision_id,
652
650
               parameter which can be either an integer revno or a
653
651
               string hash."""
654
652
        from bzrlib.inventory import Inventory
655
 
        from bzrlib.xml import unpack_xml
656
653
 
657
 
        return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
 
654
        f = self.get_inventory_xml_file(inventory_id)
 
655
        return bzrlib.xml.serializer_v4.read_inventory(f)
658
656
 
659
657
 
660
658
    def get_inventory_xml(self, inventory_id):
661
659
        """Get inventory XML as a file object."""
662
660
        return self.inventory_store[inventory_id]
 
661
 
 
662
    get_inventory_xml_file = get_inventory_xml
663
663
            
664
664
 
665
665
    def get_inventory_sha1(self, inventory_id):
876
876
 
877
877
    def lookup_revision(self, revision):
878
878
        """Return the revision identifier for a given revision information."""
879
 
        revno, info = self.get_revision_info(revision)
 
879
        revno, info = self._get_revision_info(revision)
880
880
        return info
881
881
 
882
882
 
897
897
        revision can also be a string, in which case it is parsed for something like
898
898
            'date:' or 'revid:' etc.
899
899
        """
 
900
        revno, rev_id = self._get_revision_info(revision)
 
901
        if revno is None:
 
902
            raise bzrlib.errors.NoSuchRevision(self, revision)
 
903
        return revno, rev_id
 
904
 
 
905
    def get_rev_id(self, revno, history=None):
 
906
        """Find the revision id of the specified revno."""
 
907
        if revno == 0:
 
908
            return None
 
909
        if history is None:
 
910
            history = self.revision_history()
 
911
        elif revno <= 0 or revno > len(history):
 
912
            raise bzrlib.errors.NoSuchRevision(self, revno)
 
913
        return history[revno - 1]
 
914
 
 
915
    def _get_revision_info(self, revision):
 
916
        """Return (revno, revision id) for revision specifier.
 
917
 
 
918
        revision can be an integer, in which case it is assumed to be revno
 
919
        (though this will translate negative values into positive ones)
 
920
        revision can also be a string, in which case it is parsed for something
 
921
        like 'date:' or 'revid:' etc.
 
922
 
 
923
        A revid is always returned.  If it is None, the specifier referred to
 
924
        the null revision.  If the revid does not occur in the revision
 
925
        history, revno will be None.
 
926
        """
 
927
        
900
928
        if revision is None:
901
929
            return 0, None
902
930
        revno = None
906
934
            pass
907
935
        revs = self.revision_history()
908
936
        if isinstance(revision, int):
909
 
            if revision == 0:
910
 
                return 0, None
911
 
            # Mabye we should do this first, but we don't need it if revision == 0
912
937
            if revision < 0:
913
938
                revno = len(revs) + revision + 1
914
939
            else:
915
940
                revno = revision
 
941
            rev_id = self.get_rev_id(revno, revs)
916
942
        elif isinstance(revision, basestring):
917
943
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
918
944
                if revision.startswith(prefix):
919
 
                    revno = func(self, revs, revision)
 
945
                    result = func(self, revs, revision)
 
946
                    if len(result) > 1:
 
947
                        revno, rev_id = result
 
948
                    else:
 
949
                        revno = result[0]
 
950
                        rev_id = self.get_rev_id(revno, revs)
920
951
                    break
921
952
            else:
922
 
                raise BzrError('No namespace registered for string: %r' % revision)
 
953
                raise BzrError('No namespace registered for string: %r' %
 
954
                               revision)
 
955
        else:
 
956
            raise TypeError('Unhandled revision type %s' % revision)
923
957
 
924
 
        if revno is None or revno <= 0 or revno > len(revs):
925
 
            raise BzrError("no such revision %s" % revision)
926
 
        return revno, revs[revno-1]
 
958
        if revno is None:
 
959
            if rev_id is None:
 
960
                raise bzrlib.errors.NoSuchRevision(self, revision)
 
961
        return revno, rev_id
927
962
 
928
963
    def _namespace_revno(self, revs, revision):
929
964
        """Lookup a revision by revision number"""
930
965
        assert revision.startswith('revno:')
931
966
        try:
932
 
            return int(revision[6:])
 
967
            return (int(revision[6:]),)
933
968
        except ValueError:
934
969
            return None
935
970
    REVISION_NAMESPACES['revno:'] = _namespace_revno
936
971
 
937
972
    def _namespace_revid(self, revs, revision):
938
973
        assert revision.startswith('revid:')
 
974
        rev_id = revision[len('revid:'):]
939
975
        try:
940
 
            return revs.index(revision[6:]) + 1
 
976
            return revs.index(rev_id) + 1, rev_id
941
977
        except ValueError:
942
 
            return None
 
978
            return None, rev_id
943
979
    REVISION_NAMESPACES['revid:'] = _namespace_revid
944
980
 
945
981
    def _namespace_last(self, revs, revision):
947
983
        try:
948
984
            offset = int(revision[5:])
949
985
        except ValueError:
950
 
            return None
 
986
            return (None,)
951
987
        else:
952
988
            if offset <= 0:
953
989
                raise BzrError('You must supply a positive value for --revision last:XXX')
954
 
            return len(revs) - offset + 1
 
990
            return (len(revs) - offset + 1,)
955
991
    REVISION_NAMESPACES['last:'] = _namespace_last
956
992
 
957
993
    def _namespace_tag(self, revs, revision):
1032
1068
                # TODO: Handle timezone.
1033
1069
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1034
1070
                if first >= dt and (last is None or dt >= last):
1035
 
                    return i+1
 
1071
                    return (i+1,)
1036
1072
        else:
1037
1073
            for i in range(len(revs)):
1038
1074
                r = self.get_revision(revs[i])
1039
1075
                # TODO: Handle timezone.
1040
1076
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1041
1077
                if first <= dt and (last is None or dt <= last):
1042
 
                    return i+1
 
1078
                    return (i+1,)
1043
1079
    REVISION_NAMESPACES['date:'] = _namespace_date
1044
1080
 
1045
1081
    def revision_tree(self, revision_id):
1280
1316
            self.unlock()
1281
1317
 
1282
1318
 
 
1319
    def get_parent(self):
 
1320
        """Return the parent location of the branch.
 
1321
 
 
1322
        This is the default location for push/pull/missing.  The usual
 
1323
        pattern is that the user can override it by specifying a
 
1324
        location.
 
1325
        """
 
1326
        import errno
 
1327
        _locs = ['parent', 'pull', 'x-pull']
 
1328
        for l in _locs:
 
1329
            try:
 
1330
                return self.controlfile(l, 'r').read().strip('\n')
 
1331
            except IOError, e:
 
1332
                if e.errno != errno.ENOENT:
 
1333
                    raise
 
1334
        return None
 
1335
 
 
1336
 
 
1337
    def set_parent(self, url):
 
1338
        # TODO: Maybe delete old location files?
 
1339
        from bzrlib.atomicfile import AtomicFile
 
1340
        self.lock_write()
 
1341
        try:
 
1342
            f = AtomicFile(self.controlfilename('parent'))
 
1343
            try:
 
1344
                f.write(url + '\n')
 
1345
                f.commit()
 
1346
            finally:
 
1347
                f.close()
 
1348
        finally:
 
1349
            self.unlock()
 
1350
 
 
1351
    def check_revno(self, revno):
 
1352
        """\
 
1353
        Check whether a revno corresponds to any revision.
 
1354
        Zero (the NULL revision) is considered valid.
 
1355
        """
 
1356
        if revno != 0:
 
1357
            self.check_real_revno(revno)
 
1358
            
 
1359
    def check_real_revno(self, revno):
 
1360
        """\
 
1361
        Check whether a revno corresponds to a real revision.
 
1362
        Zero (the NULL revision) is considered invalid
 
1363
        """
 
1364
        if revno < 1 or revno > self.revno():
 
1365
            raise InvalidRevisionNumber(revno)
 
1366
        
 
1367
        
 
1368
 
1283
1369
 
1284
1370
class ScratchBranch(Branch):
1285
1371
    """Special test class: a branch that cleans up after itself.
1327
1413
        os.rmdir(base)
1328
1414
        copytree(self.base, base, symlinks=True)
1329
1415
        return ScratchBranch(base=base)
 
1416
 
 
1417
 
1330
1418
        
1331
1419
    def __del__(self):
1332
1420
        self.destroy()
1415
1503
def copy_branch(branch_from, to_location, revision=None):
1416
1504
    """Copy branch_from into the existing directory to_location.
1417
1505
 
1418
 
    If revision is not None, the head of the new branch will be revision.
 
1506
    revision
 
1507
        If not None, only revisions up to this point will be copied.
 
1508
        The head of the new branch will be that revision.
 
1509
 
 
1510
    to_location
 
1511
        The name of a local directory that exists but is empty.
1419
1512
    """
1420
1513
    from bzrlib.merge import merge
1421
1514
    from bzrlib.branch import Branch
 
1515
 
 
1516
    assert isinstance(branch_from, Branch)
 
1517
    assert isinstance(to_location, basestring)
 
1518
    
1422
1519
    br_to = Branch(to_location, init=True)
1423
1520
    br_to.set_root_id(branch_from.get_root_id())
1424
1521
    if revision is None:
1428
1525
    br_to.update_revisions(branch_from, stop_revision=revno)
1429
1526
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1430
1527
          check_clean=False, ignore_zero=True)
 
1528
    
1431
1529
    from_location = pull_loc(branch_from)
1432
 
    br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
1530
    br_to.set_parent(pull_loc(branch_from))
1433
1531