~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Lalo Martins
  • Date: 2005-09-07 15:28:14 UTC
  • mto: (1185.1.22)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: lalo@exoweb.net-20050907152813-da20c1411bc3be7f
moving the 'revision spec' stuff out of the Branch class and into a new
module (obeying a XXX comment).

This is the last XXX in branch.py, except for the ones I added :-)

Show diffs side-by-side

added added

removed removed

Lines of Context:
143
143
        Base directory/url of the branch.
144
144
    """
145
145
    base = None
146
 
    
147
 
    # Map some sort of prefix into a namespace
148
 
    # stuff like "revno:10", "revid:", etc.
149
 
    # This should match a prefix with a function which accepts
150
 
    REVISION_NAMESPACES = {}
151
146
 
152
147
    def __new__(cls, *a, **kw):
153
148
        """this is temporary, till we get rid of all code that does
154
149
        b = Branch()
155
150
        """
156
 
        # AAARGH!  MY EYES!  UUUUGLY!!!
 
151
        # XXX: AAARGH!  MY EYES!  UUUUGLY!!!
157
152
        if cls == Branch:
158
153
            cls = LocalBranch
159
154
        b = object.__new__(cls)
160
155
        return b
161
156
 
162
 
    def _namespace_revno(self, revs, revision):
163
 
        """Lookup a revision by revision number"""
164
 
        assert revision.startswith('revno:')
165
 
        try:
166
 
            return (int(revision[6:]),)
167
 
        except ValueError:
168
 
            return None
169
 
    REVISION_NAMESPACES['revno:'] = _namespace_revno
170
 
 
171
 
    def _namespace_revid(self, revs, revision):
172
 
        assert revision.startswith('revid:')
173
 
        rev_id = revision[len('revid:'):]
174
 
        try:
175
 
            return revs.index(rev_id) + 1, rev_id
176
 
        except ValueError:
177
 
            return None, rev_id
178
 
    REVISION_NAMESPACES['revid:'] = _namespace_revid
179
 
 
180
 
    def _namespace_last(self, revs, revision):
181
 
        assert revision.startswith('last:')
182
 
        try:
183
 
            offset = int(revision[5:])
184
 
        except ValueError:
185
 
            return (None,)
186
 
        else:
187
 
            if offset <= 0:
188
 
                raise BzrError('You must supply a positive value for --revision last:XXX')
189
 
            return (len(revs) - offset + 1,)
190
 
    REVISION_NAMESPACES['last:'] = _namespace_last
191
 
 
192
 
    def _namespace_tag(self, revs, revision):
193
 
        assert revision.startswith('tag:')
194
 
        raise BzrError('tag: namespace registered, but not implemented.')
195
 
    REVISION_NAMESPACES['tag:'] = _namespace_tag
196
 
 
197
 
    def _namespace_date(self, revs, revision):
198
 
        assert revision.startswith('date:')
199
 
        import datetime
200
 
        # Spec for date revisions:
201
 
        #   date:value
202
 
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
203
 
        #   it can also start with a '+/-/='. '+' says match the first
204
 
        #   entry after the given date. '-' is match the first entry before the date
205
 
        #   '=' is match the first entry after, but still on the given date.
206
 
        #
207
 
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
208
 
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
209
 
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
210
 
        #       May 13th, 2005 at 0:00
211
 
        #
212
 
        #   So the proper way of saying 'give me all entries for today' is:
213
 
        #       -r {date:+today}:{date:-tomorrow}
214
 
        #   The default is '=' when not supplied
215
 
        val = revision[5:]
216
 
        match_style = '='
217
 
        if val[:1] in ('+', '-', '='):
218
 
            match_style = val[:1]
219
 
            val = val[1:]
220
 
 
221
 
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
222
 
        if val.lower() == 'yesterday':
223
 
            dt = today - datetime.timedelta(days=1)
224
 
        elif val.lower() == 'today':
225
 
            dt = today
226
 
        elif val.lower() == 'tomorrow':
227
 
            dt = today + datetime.timedelta(days=1)
228
 
        else:
229
 
            import re
230
 
            # This should be done outside the function to avoid recompiling it.
231
 
            _date_re = re.compile(
232
 
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
233
 
                    r'(,|T)?\s*'
234
 
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
235
 
                )
236
 
            m = _date_re.match(val)
237
 
            if not m or (not m.group('date') and not m.group('time')):
238
 
                raise BzrError('Invalid revision date %r' % revision)
239
 
 
240
 
            if m.group('date'):
241
 
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
242
 
            else:
243
 
                year, month, day = today.year, today.month, today.day
244
 
            if m.group('time'):
245
 
                hour = int(m.group('hour'))
246
 
                minute = int(m.group('minute'))
247
 
                if m.group('second'):
248
 
                    second = int(m.group('second'))
249
 
                else:
250
 
                    second = 0
251
 
            else:
252
 
                hour, minute, second = 0,0,0
253
 
 
254
 
            dt = datetime.datetime(year=year, month=month, day=day,
255
 
                    hour=hour, minute=minute, second=second)
256
 
        first = dt
257
 
        last = None
258
 
        reversed = False
259
 
        if match_style == '-':
260
 
            reversed = True
261
 
        elif match_style == '=':
262
 
            last = dt + datetime.timedelta(days=1)
263
 
 
264
 
        if reversed:
265
 
            for i in range(len(revs)-1, -1, -1):
266
 
                r = self.get_revision(revs[i])
267
 
                # TODO: Handle timezone.
268
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
269
 
                if first >= dt and (last is None or dt >= last):
270
 
                    return (i+1,)
271
 
        else:
272
 
            for i in range(len(revs)):
273
 
                r = self.get_revision(revs[i])
274
 
                # TODO: Handle timezone.
275
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
276
 
                if first <= dt and (last is None or dt <= last):
277
 
                    return (i+1,)
278
 
    REVISION_NAMESPACES['date:'] = _namespace_date
279
 
 
280
157
 
281
158
class LocalBranch(Branch):
282
159
    """A branch stored in the actual filesystem.
1007
884
        
1008
885
 
1009
886
    def lookup_revision(self, revision):
1010
 
        """Return the revision identifier for a given revision information."""
1011
 
        revno, info = self._get_revision_info(revision)
 
887
        """Return the revision identifier for a given revision specifier."""
 
888
        # XXX: I'm not sure this method belongs here; I'd rather have the
 
889
        # revision spec stuff be an UI thing, and branch blissfully unaware
 
890
        # of it.
 
891
        # Also, I'm not entirely happy with this method returning None
 
892
        # when the revision doesn't exist.
 
893
        # But I'm keeping the contract I found, because this seems to be
 
894
        # used in a lot of places - and when I do change these, I'd rather
 
895
        # figure out case-by-case which ones actually want to care about
 
896
        # revision specs (eg, they are UI-level) and which ones should trust
 
897
        # that they have a revno/revid.
 
898
        #   -- lalo@exoweb.net, 2005-09-07
 
899
        from bzrlib.errors import NoSuchRevision
 
900
        from bzrlib.revisionspec import get_revision_info
 
901
        try:
 
902
            revno, info = get_revision_info(self, revision)
 
903
        except NoSuchRevision:
 
904
            return None
1012
905
        return info
1013
906
 
1014
907
 
1021
914
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
1022
915
 
1023
916
 
1024
 
    def get_revision_info(self, revision):
1025
 
        """Return (revno, revision id) for revision identifier.
1026
 
 
1027
 
        revision can be an integer, in which case it is assumed to be revno (though
1028
 
            this will translate negative values into positive ones)
1029
 
        revision can also be a string, in which case it is parsed for something like
1030
 
            'date:' or 'revid:' etc.
1031
 
        """
1032
 
        revno, rev_id = self._get_revision_info(revision)
1033
 
        if revno is None:
1034
 
            raise bzrlib.errors.NoSuchRevision(self, revision)
1035
 
        return revno, rev_id
1036
 
 
1037
917
    def get_rev_id(self, revno, history=None):
1038
918
        """Find the revision id of the specified revno."""
1039
919
        if revno == 0:
1044
924
            raise bzrlib.errors.NoSuchRevision(self, revno)
1045
925
        return history[revno - 1]
1046
926
 
1047
 
    def _get_revision_info(self, revision):
1048
 
        """Return (revno, revision id) for revision specifier.
1049
 
 
1050
 
        revision can be an integer, in which case it is assumed to be revno
1051
 
        (though this will translate negative values into positive ones)
1052
 
        revision can also be a string, in which case it is parsed for something
1053
 
        like 'date:' or 'revid:' etc.
1054
 
 
1055
 
        A revid is always returned.  If it is None, the specifier referred to
1056
 
        the null revision.  If the revid does not occur in the revision
1057
 
        history, revno will be None.
1058
 
        """
1059
 
        
1060
 
        if revision is None:
1061
 
            return 0, None
1062
 
        revno = None
1063
 
        try:# Convert to int if possible
1064
 
            revision = int(revision)
1065
 
        except ValueError:
1066
 
            pass
1067
 
        revs = self.revision_history()
1068
 
        if isinstance(revision, int):
1069
 
            if revision < 0:
1070
 
                revno = len(revs) + revision + 1
1071
 
            else:
1072
 
                revno = revision
1073
 
            rev_id = self.get_rev_id(revno, revs)
1074
 
        elif isinstance(revision, basestring):
1075
 
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
1076
 
                if revision.startswith(prefix):
1077
 
                    result = func(self, revs, revision)
1078
 
                    if len(result) > 1:
1079
 
                        revno, rev_id = result
1080
 
                    else:
1081
 
                        revno = result[0]
1082
 
                        rev_id = self.get_rev_id(revno, revs)
1083
 
                    break
1084
 
            else:
1085
 
                raise BzrError('No namespace registered for string: %r' %
1086
 
                               revision)
1087
 
        else:
1088
 
            raise TypeError('Unhandled revision type %s' % revision)
1089
 
 
1090
 
        if revno is None:
1091
 
            if rev_id is None:
1092
 
                raise bzrlib.errors.NoSuchRevision(self, revision)
1093
 
        return revno, rev_id
1094
 
 
1095
927
    def revision_tree(self, revision_id):
1096
928
        """Return Tree for a revision on this branch.
1097
929
 
1516
1348
        The name of a local directory that exists but is empty.
1517
1349
    """
1518
1350
    from bzrlib.merge import merge
 
1351
    from bzrlib.revisionspec import get_revision_info
1519
1352
 
1520
1353
    assert isinstance(branch_from, Branch)
1521
1354
    assert isinstance(to_location, basestring)
1525
1358
    if revision is None:
1526
1359
        revno = branch_from.revno()
1527
1360
    else:
1528
 
        revno, rev_id = branch_from.get_revision_info(revision)
 
1361
        revno, rev_id = get_revision_info(branch_from, revision)
1529
1362
    br_to.update_revisions(branch_from, stop_revision=revno)
1530
1363
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1531
1364
          check_clean=False, ignore_zero=True)