~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-07-11 06:13:18 UTC
  • mfrom: (unknown (missing))
  • Revision ID: mbp@sourcefrog.net-20050711061318-80557a9f045b1f38
- merge john's revision-naming code

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
 
153
158
    def __init__(self, base, init=False, find_root=True):
154
159
        """Create new branch object at a particular location.
155
160
 
461
466
            # use inventory as it was in that revision
462
467
            file_id = tree.inventory.path2id(file)
463
468
            if not file_id:
464
 
                raise BzrError("%r is not present in revision %d" % (file, revno))
 
469
                raise BzrError("%r is not present in revision %s" % (file, revno))
465
470
            tree.print_file(file_id)
466
471
        finally:
467
472
            self.unlock()
836
841
        commit(self, *args, **kw)
837
842
        
838
843
 
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
 
 
 
844
    def lookup_revision(self, revision):
 
845
        """Return the revision identifier for a given revision information."""
 
846
        revno, info = self.get_revision_info(revision)
 
847
        return info
 
848
 
 
849
    def get_revision_info(self, revision):
 
850
        """Return (revno, revision id) for revision identifier.
 
851
 
 
852
        revision can be an integer, in which case it is assumed to be revno (though
 
853
            this will translate negative values into positive ones)
 
854
        revision can also be a string, in which case it is parsed for something like
 
855
            'date:' or 'revid:' etc.
 
856
        """
 
857
        if revision is None:
 
858
            return 0, None
 
859
        revno = None
 
860
        try:# Convert to int if possible
 
861
            revision = int(revision)
 
862
        except ValueError:
 
863
            pass
 
864
        revs = self.revision_history()
 
865
        if isinstance(revision, int):
 
866
            if revision == 0:
 
867
                return 0, None
 
868
            # Mabye we should do this first, but we don't need it if revision == 0
 
869
            if revision < 0:
 
870
                revno = len(revs) + revision + 1
 
871
            else:
 
872
                revno = revision
 
873
        elif isinstance(revision, basestring):
 
874
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
 
875
                if revision.startswith(prefix):
 
876
                    revno = func(self, revs, revision)
 
877
                    break
 
878
            else:
 
879
                raise BzrError('No namespace registered for string: %r' % revision)
 
880
 
 
881
        if revno is None or revno <= 0 or revno > len(revs):
 
882
            raise BzrError("no such revision %s" % revision)
 
883
        return revno, revs[revno-1]
 
884
 
 
885
    def _namespace_revno(self, revs, revision):
 
886
        """Lookup a revision by revision number"""
 
887
        assert revision.startswith('revno:')
 
888
        try:
 
889
            return int(revision[6:])
 
890
        except ValueError:
 
891
            return None
 
892
    REVISION_NAMESPACES['revno:'] = _namespace_revno
 
893
 
 
894
    def _namespace_revid(self, revs, revision):
 
895
        assert revision.startswith('revid:')
 
896
        try:
 
897
            return revs.index(revision[6:]) + 1
 
898
        except ValueError:
 
899
            return None
 
900
    REVISION_NAMESPACES['revid:'] = _namespace_revid
 
901
 
 
902
    def _namespace_last(self, revs, revision):
 
903
        assert revision.startswith('last:')
 
904
        try:
 
905
            offset = int(revision[5:])
 
906
        except ValueError:
 
907
            return None
 
908
        else:
 
909
            if offset <= 0:
 
910
                raise BzrError('You must supply a positive value for --revision last:XXX')
 
911
            return len(revs) - offset + 1
 
912
    REVISION_NAMESPACES['last:'] = _namespace_last
 
913
 
 
914
    def _namespace_tag(self, revs, revision):
 
915
        assert revision.startswith('tag:')
 
916
        raise BzrError('tag: namespace registered, but not implemented.')
 
917
    REVISION_NAMESPACES['tag:'] = _namespace_tag
 
918
 
 
919
    def _namespace_date(self, revs, revision):
 
920
        assert revision.startswith('date:')
 
921
        import datetime
 
922
        # Spec for date revisions:
 
923
        #   date:value
 
924
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
 
925
        #   it can also start with a '+/-/='. '+' says match the first
 
926
        #   entry after the given date. '-' is match the first entry before the date
 
927
        #   '=' is match the first entry after, but still on the given date.
 
928
        #
 
929
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
 
930
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
 
931
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
 
932
        #       May 13th, 2005 at 0:00
 
933
        #
 
934
        #   So the proper way of saying 'give me all entries for today' is:
 
935
        #       -r {date:+today}:{date:-tomorrow}
 
936
        #   The default is '=' when not supplied
 
937
        val = revision[5:]
 
938
        match_style = '='
 
939
        if val[:1] in ('+', '-', '='):
 
940
            match_style = val[:1]
 
941
            val = val[1:]
 
942
 
 
943
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
 
944
        if val.lower() == 'yesterday':
 
945
            dt = today - datetime.timedelta(days=1)
 
946
        elif val.lower() == 'today':
 
947
            dt = today
 
948
        elif val.lower() == 'tomorrow':
 
949
            dt = today + datetime.timedelta(days=1)
 
950
        else:
 
951
            # This should be done outside the function to avoid recompiling it.
 
952
            _date_re = re.compile(
 
953
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
 
954
                    r'(,|T)?\s*'
 
955
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
 
956
                )
 
957
            m = _date_re.match(val)
 
958
            if not m or (not m.group('date') and not m.group('time')):
 
959
                raise BzrError('Invalid revision date %r' % revision)
 
960
 
 
961
            if m.group('date'):
 
962
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
 
963
            else:
 
964
                year, month, day = today.year, today.month, today.day
 
965
            if m.group('time'):
 
966
                hour = int(m.group('hour'))
 
967
                minute = int(m.group('minute'))
 
968
                if m.group('second'):
 
969
                    second = int(m.group('second'))
 
970
                else:
 
971
                    second = 0
 
972
            else:
 
973
                hour, minute, second = 0,0,0
 
974
 
 
975
            dt = datetime.datetime(year=year, month=month, day=day,
 
976
                    hour=hour, minute=minute, second=second)
 
977
        first = dt
 
978
        last = None
 
979
        reversed = False
 
980
        if match_style == '-':
 
981
            reversed = True
 
982
        elif match_style == '=':
 
983
            last = dt + datetime.timedelta(days=1)
 
984
 
 
985
        if reversed:
 
986
            for i in range(len(revs)-1, -1, -1):
 
987
                r = self.get_revision(revs[i])
 
988
                # TODO: Handle timezone.
 
989
                dt = datetime.datetime.fromtimestamp(r.timestamp)
 
990
                if first >= dt and (last is None or dt >= last):
 
991
                    return i+1
 
992
        else:
 
993
            for i in range(len(revs)):
 
994
                r = self.get_revision(revs[i])
 
995
                # TODO: Handle timezone.
 
996
                dt = datetime.datetime.fromtimestamp(r.timestamp)
 
997
                if first <= dt and (last is None or dt <= last):
 
998
                    return i+1
 
999
    REVISION_NAMESPACES['date:'] = _namespace_date
850
1000
 
851
1001
    def revision_tree(self, revision_id):
852
1002
        """Return Tree for a revision on this branch.