~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-07-07 10:31:36 UTC
  • Revision ID: mbp@sourcefrog.net-20050707103135-9b4d911d8df6e880
- fix pwk help

Show diffs side-by-side

added added

removed removed

Lines of Context:
150
150
    _lock_count = None
151
151
    _lock = None
152
152
    
153
 
    # Map some sort of prefix into a namespace
154
 
    # stuff like "revno:10", "revid:", etc.
155
 
    # This should match a prefix with a function which accepts
156
 
    REVISION_NAMESPACES = {}
157
 
 
158
153
    def __init__(self, base, init=False, find_root=True):
159
154
        """Create new branch object at a particular location.
160
155
 
312
307
            self.controlfile(f, 'w').write('')
313
308
        mutter('created control directory in ' + self.base)
314
309
 
315
 
        pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
 
310
        pack_xml(Inventory(), self.controlfile('inventory','w'))
316
311
 
317
312
 
318
313
    def _check_format(self):
333
328
                           ['use a different bzr version',
334
329
                            'or remove the .bzr directory and "bzr init" again'])
335
330
 
336
 
    def get_root_id(self):
337
 
        """Return the id of this branches root"""
338
 
        inv = self.read_working_inventory()
339
 
        return inv.root.file_id
340
331
 
341
 
    def set_root_id(self, file_id):
342
 
        inv = self.read_working_inventory()
343
 
        orig_root_id = inv.root.file_id
344
 
        del inv._byid[inv.root.file_id]
345
 
        inv.root.file_id = file_id
346
 
        inv._byid[inv.root.file_id] = inv.root
347
 
        for fid in inv:
348
 
            entry = inv[fid]
349
 
            if entry.parent_id in (None, orig_root_id):
350
 
                entry.parent_id = inv.root.file_id
351
 
        self._write_inventory(inv)
352
332
 
353
333
    def read_working_inventory(self):
354
334
        """Read the working inventory."""
361
341
            # ElementTree does its own conversion from UTF-8, so open in
362
342
            # binary.
363
343
            inv = unpack_xml(Inventory,
364
 
                             self.controlfile('inventory', 'rb'))
 
344
                                  self.controlfile('inventory', 'rb'))
365
345
            mutter("loaded inventory of %d items in %f"
366
346
                   % (len(inv), time() - before))
367
347
            return inv
481
461
            # use inventory as it was in that revision
482
462
            file_id = tree.inventory.path2id(file)
483
463
            if not file_id:
484
 
                raise BzrError("%r is not present in revision %s" % (file, revno))
 
464
                raise BzrError("%r is not present in revision %d" % (file, revno))
485
465
            tree.print_file(file_id)
486
466
        finally:
487
467
            self.unlock()
536
516
    # FIXME: this doesn't need to be a branch method
537
517
    def set_inventory(self, new_inventory_list):
538
518
        from bzrlib.inventory import Inventory, InventoryEntry
539
 
        inv = Inventory(self.get_root_id())
 
519
        inv = Inventory()
540
520
        for path, file_id, parent, kind in new_inventory_list:
541
521
            name = os.path.basename(path)
542
522
            if name == "":
564
544
        return self.working_tree().unknowns()
565
545
 
566
546
 
567
 
    def append_revision(self, *revision_ids):
 
547
    def append_revision(self, revision_id):
568
548
        from bzrlib.atomicfile import AtomicFile
569
549
 
570
 
        for revision_id in revision_ids:
571
 
            mutter("add {%s} to revision-history" % revision_id)
572
 
 
573
 
        rev_history = self.revision_history()
574
 
        rev_history.extend(revision_ids)
 
550
        mutter("add {%s} to revision-history" % revision_id)
 
551
        rev_history = self.revision_history() + [revision_id]
575
552
 
576
553
        f = AtomicFile(self.controlfilename('revision-history'))
577
554
        try:
634
611
        # must be the same as its revision, so this is trivial.
635
612
        if revision_id == None:
636
613
            from bzrlib.inventory import Inventory
637
 
            return Inventory(self.get_root_id())
 
614
            return Inventory()
638
615
        else:
639
616
            return self.get_inventory(revision_id)
640
617
 
806
783
        True
807
784
        """
808
785
        from bzrlib.progress import ProgressBar
 
786
        try:
 
787
            set
 
788
        except NameError:
 
789
            from sets import Set as set
809
790
 
810
791
        pb = ProgressBar()
811
792
 
855
836
        commit(self, *args, **kw)
856
837
        
857
838
 
858
 
    def lookup_revision(self, revision):
859
 
        """Return the revision identifier for a given revision information."""
860
 
        revno, info = self.get_revision_info(revision)
861
 
        return info
862
 
 
863
 
    def get_revision_info(self, revision):
864
 
        """Return (revno, revision id) for revision identifier.
865
 
 
866
 
        revision can be an integer, in which case it is assumed to be revno (though
867
 
            this will translate negative values into positive ones)
868
 
        revision can also be a string, in which case it is parsed for something like
869
 
            'date:' or 'revid:' etc.
870
 
        """
871
 
        if revision is None:
872
 
            return 0, None
873
 
        revno = None
874
 
        try:# Convert to int if possible
875
 
            revision = int(revision)
876
 
        except ValueError:
877
 
            pass
878
 
        revs = self.revision_history()
879
 
        if isinstance(revision, int):
880
 
            if revision == 0:
881
 
                return 0, None
882
 
            # Mabye we should do this first, but we don't need it if revision == 0
883
 
            if revision < 0:
884
 
                revno = len(revs) + revision + 1
885
 
            else:
886
 
                revno = revision
887
 
        elif isinstance(revision, basestring):
888
 
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
889
 
                if revision.startswith(prefix):
890
 
                    revno = func(self, revs, revision)
891
 
                    break
892
 
            else:
893
 
                raise BzrError('No namespace registered for string: %r' % revision)
894
 
 
895
 
        if revno is None or revno <= 0 or revno > len(revs):
896
 
            raise BzrError("no such revision %s" % revision)
897
 
        return revno, revs[revno-1]
898
 
 
899
 
    def _namespace_revno(self, revs, revision):
900
 
        """Lookup a revision by revision number"""
901
 
        assert revision.startswith('revno:')
902
 
        try:
903
 
            return int(revision[6:])
904
 
        except ValueError:
905
 
            return None
906
 
    REVISION_NAMESPACES['revno:'] = _namespace_revno
907
 
 
908
 
    def _namespace_revid(self, revs, revision):
909
 
        assert revision.startswith('revid:')
910
 
        try:
911
 
            return revs.index(revision[6:]) + 1
912
 
        except ValueError:
913
 
            return None
914
 
    REVISION_NAMESPACES['revid:'] = _namespace_revid
915
 
 
916
 
    def _namespace_last(self, revs, revision):
917
 
        assert revision.startswith('last:')
918
 
        try:
919
 
            offset = int(revision[5:])
920
 
        except ValueError:
921
 
            return None
922
 
        else:
923
 
            if offset <= 0:
924
 
                raise BzrError('You must supply a positive value for --revision last:XXX')
925
 
            return len(revs) - offset + 1
926
 
    REVISION_NAMESPACES['last:'] = _namespace_last
927
 
 
928
 
    def _namespace_tag(self, revs, revision):
929
 
        assert revision.startswith('tag:')
930
 
        raise BzrError('tag: namespace registered, but not implemented.')
931
 
    REVISION_NAMESPACES['tag:'] = _namespace_tag
932
 
 
933
 
    def _namespace_date(self, revs, revision):
934
 
        assert revision.startswith('date:')
935
 
        import datetime
936
 
        # Spec for date revisions:
937
 
        #   date:value
938
 
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
939
 
        #   it can also start with a '+/-/='. '+' says match the first
940
 
        #   entry after the given date. '-' is match the first entry before the date
941
 
        #   '=' is match the first entry after, but still on the given date.
942
 
        #
943
 
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
944
 
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
945
 
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
946
 
        #       May 13th, 2005 at 0:00
947
 
        #
948
 
        #   So the proper way of saying 'give me all entries for today' is:
949
 
        #       -r {date:+today}:{date:-tomorrow}
950
 
        #   The default is '=' when not supplied
951
 
        val = revision[5:]
952
 
        match_style = '='
953
 
        if val[:1] in ('+', '-', '='):
954
 
            match_style = val[:1]
955
 
            val = val[1:]
956
 
 
957
 
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
958
 
        if val.lower() == 'yesterday':
959
 
            dt = today - datetime.timedelta(days=1)
960
 
        elif val.lower() == 'today':
961
 
            dt = today
962
 
        elif val.lower() == 'tomorrow':
963
 
            dt = today + datetime.timedelta(days=1)
964
 
        else:
965
 
            import re
966
 
            # This should be done outside the function to avoid recompiling it.
967
 
            _date_re = re.compile(
968
 
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
969
 
                    r'(,|T)?\s*'
970
 
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
971
 
                )
972
 
            m = _date_re.match(val)
973
 
            if not m or (not m.group('date') and not m.group('time')):
974
 
                raise BzrError('Invalid revision date %r' % revision)
975
 
 
976
 
            if m.group('date'):
977
 
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
978
 
            else:
979
 
                year, month, day = today.year, today.month, today.day
980
 
            if m.group('time'):
981
 
                hour = int(m.group('hour'))
982
 
                minute = int(m.group('minute'))
983
 
                if m.group('second'):
984
 
                    second = int(m.group('second'))
985
 
                else:
986
 
                    second = 0
987
 
            else:
988
 
                hour, minute, second = 0,0,0
989
 
 
990
 
            dt = datetime.datetime(year=year, month=month, day=day,
991
 
                    hour=hour, minute=minute, second=second)
992
 
        first = dt
993
 
        last = None
994
 
        reversed = False
995
 
        if match_style == '-':
996
 
            reversed = True
997
 
        elif match_style == '=':
998
 
            last = dt + datetime.timedelta(days=1)
999
 
 
1000
 
        if reversed:
1001
 
            for i in range(len(revs)-1, -1, -1):
1002
 
                r = self.get_revision(revs[i])
1003
 
                # TODO: Handle timezone.
1004
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1005
 
                if first >= dt and (last is None or dt >= last):
1006
 
                    return i+1
1007
 
        else:
1008
 
            for i in range(len(revs)):
1009
 
                r = self.get_revision(revs[i])
1010
 
                # TODO: Handle timezone.
1011
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1012
 
                if first <= dt and (last is None or dt <= last):
1013
 
                    return i+1
1014
 
    REVISION_NAMESPACES['date:'] = _namespace_date
 
839
    def lookup_revision(self, revno):
 
840
        """Return revision hash for revision number."""
 
841
        if revno == 0:
 
842
            return None
 
843
 
 
844
        try:
 
845
            # list is 0-based; revisions are 1-based
 
846
            return self.revision_history()[revno-1]
 
847
        except IndexError:
 
848
            raise BzrError("no such revision %s" % revno)
 
849
 
1015
850
 
1016
851
    def revision_tree(self, revision_id):
1017
852
        """Return Tree for a revision on this branch.
1022
857
        # TODO: refactor this to use an existing revision object
1023
858
        # so we don't need to read it in twice.
1024
859
        if revision_id == None:
1025
 
            return EmptyTree(self.get_root_id())
 
860
            return EmptyTree()
1026
861
        else:
1027
862
            inv = self.get_revision_inventory(revision_id)
1028
863
            return RevisionTree(self.text_store, inv)
1042
877
        from bzrlib.tree import EmptyTree, RevisionTree
1043
878
        r = self.last_patch()
1044
879
        if r == None:
1045
 
            return EmptyTree(self.get_root_id())
 
880
            return EmptyTree()
1046
881
        else:
1047
882
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
1048
883
 
1365
1200
 
1366
1201
    s = hexlify(rand_bytes(8))
1367
1202
    return '-'.join((name, compact_date(time()), s))
1368
 
 
1369
 
 
1370
 
def gen_root_id():
1371
 
    """Return a new tree-root file id."""
1372
 
    return gen_file_id('TREE_ROOT')
1373