~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 13:17:26 UTC
  • mto: (1185.1.22)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: lalo@exoweb.net-20050907131726-8d8d4ab45a390a9f
splitting a "LocalBranch" class off from Branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
52
52
        from bzrlib.remotebranch import RemoteBranch
53
53
        return RemoteBranch(f, **args)
54
54
    else:
55
 
        return Branch(f, **args)
 
55
        return LocalBranch(f, **args)
56
56
 
57
57
 
58
58
def find_cached_branch(f, cache_root, **args):
140
140
    """Branch holding a history of revisions.
141
141
 
142
142
    base
143
 
        Base directory of the branch.
 
143
        Base directory/url of the branch.
 
144
    """
 
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
 
 
152
    def __new__(cls, *a, **kw):
 
153
        """this is temporary, till we get rid of all code that does
 
154
        b = Branch()
 
155
        """
 
156
        # AAARGH!  MY EYES!  UUUUGLY!!!
 
157
        if cls == Branch:
 
158
            cls = LocalBranch
 
159
        b = object.__new__(cls)
 
160
        return b
 
161
 
 
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
 
 
281
class LocalBranch(Branch):
 
282
    """A branch stored in the actual filesystem.
 
283
 
 
284
    Note that it's "local" in the context of the filesystem; it doesn't
 
285
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
 
286
    it's writable, and can be accessed via the normal filesystem API.
144
287
 
145
288
    _lock_mode
146
289
        None, or 'r' or 'w'
152
295
    _lock
153
296
        Lock object from bzrlib.lock.
154
297
    """
155
 
    base = None
 
298
    # We actually expect this class to be somewhat short-lived; part of its
 
299
    # purpose is to try to isolate what bits of the branch logic are tied to
 
300
    # filesystem access, so that in a later step, we can extricate them to
 
301
    # a separarte ("storage") class.
156
302
    _lock_mode = None
157
303
    _lock_count = None
158
304
    _lock = None
159
 
    
160
 
    # Map some sort of prefix into a namespace
161
 
    # stuff like "revno:10", "revid:", etc.
162
 
    # This should match a prefix with a function which accepts
163
 
    REVISION_NAMESPACES = {}
164
305
 
165
306
    def __init__(self, base, init=False, find_root=True):
166
307
        """Create new branch object at a particular location.
951
1092
                raise bzrlib.errors.NoSuchRevision(self, revision)
952
1093
        return revno, rev_id
953
1094
 
954
 
    def _namespace_revno(self, revs, revision):
955
 
        """Lookup a revision by revision number"""
956
 
        assert revision.startswith('revno:')
957
 
        try:
958
 
            return (int(revision[6:]),)
959
 
        except ValueError:
960
 
            return None
961
 
    REVISION_NAMESPACES['revno:'] = _namespace_revno
962
 
 
963
 
    def _namespace_revid(self, revs, revision):
964
 
        assert revision.startswith('revid:')
965
 
        rev_id = revision[len('revid:'):]
966
 
        try:
967
 
            return revs.index(rev_id) + 1, rev_id
968
 
        except ValueError:
969
 
            return None, rev_id
970
 
    REVISION_NAMESPACES['revid:'] = _namespace_revid
971
 
 
972
 
    def _namespace_last(self, revs, revision):
973
 
        assert revision.startswith('last:')
974
 
        try:
975
 
            offset = int(revision[5:])
976
 
        except ValueError:
977
 
            return (None,)
978
 
        else:
979
 
            if offset <= 0:
980
 
                raise BzrError('You must supply a positive value for --revision last:XXX')
981
 
            return (len(revs) - offset + 1,)
982
 
    REVISION_NAMESPACES['last:'] = _namespace_last
983
 
 
984
 
    def _namespace_tag(self, revs, revision):
985
 
        assert revision.startswith('tag:')
986
 
        raise BzrError('tag: namespace registered, but not implemented.')
987
 
    REVISION_NAMESPACES['tag:'] = _namespace_tag
988
 
 
989
 
    def _namespace_date(self, revs, revision):
990
 
        assert revision.startswith('date:')
991
 
        import datetime
992
 
        # Spec for date revisions:
993
 
        #   date:value
994
 
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
995
 
        #   it can also start with a '+/-/='. '+' says match the first
996
 
        #   entry after the given date. '-' is match the first entry before the date
997
 
        #   '=' is match the first entry after, but still on the given date.
998
 
        #
999
 
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
1000
 
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
1001
 
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
1002
 
        #       May 13th, 2005 at 0:00
1003
 
        #
1004
 
        #   So the proper way of saying 'give me all entries for today' is:
1005
 
        #       -r {date:+today}:{date:-tomorrow}
1006
 
        #   The default is '=' when not supplied
1007
 
        val = revision[5:]
1008
 
        match_style = '='
1009
 
        if val[:1] in ('+', '-', '='):
1010
 
            match_style = val[:1]
1011
 
            val = val[1:]
1012
 
 
1013
 
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
1014
 
        if val.lower() == 'yesterday':
1015
 
            dt = today - datetime.timedelta(days=1)
1016
 
        elif val.lower() == 'today':
1017
 
            dt = today
1018
 
        elif val.lower() == 'tomorrow':
1019
 
            dt = today + datetime.timedelta(days=1)
1020
 
        else:
1021
 
            import re
1022
 
            # This should be done outside the function to avoid recompiling it.
1023
 
            _date_re = re.compile(
1024
 
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
1025
 
                    r'(,|T)?\s*'
1026
 
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
1027
 
                )
1028
 
            m = _date_re.match(val)
1029
 
            if not m or (not m.group('date') and not m.group('time')):
1030
 
                raise BzrError('Invalid revision date %r' % revision)
1031
 
 
1032
 
            if m.group('date'):
1033
 
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
1034
 
            else:
1035
 
                year, month, day = today.year, today.month, today.day
1036
 
            if m.group('time'):
1037
 
                hour = int(m.group('hour'))
1038
 
                minute = int(m.group('minute'))
1039
 
                if m.group('second'):
1040
 
                    second = int(m.group('second'))
1041
 
                else:
1042
 
                    second = 0
1043
 
            else:
1044
 
                hour, minute, second = 0,0,0
1045
 
 
1046
 
            dt = datetime.datetime(year=year, month=month, day=day,
1047
 
                    hour=hour, minute=minute, second=second)
1048
 
        first = dt
1049
 
        last = None
1050
 
        reversed = False
1051
 
        if match_style == '-':
1052
 
            reversed = True
1053
 
        elif match_style == '=':
1054
 
            last = dt + datetime.timedelta(days=1)
1055
 
 
1056
 
        if reversed:
1057
 
            for i in range(len(revs)-1, -1, -1):
1058
 
                r = self.get_revision(revs[i])
1059
 
                # TODO: Handle timezone.
1060
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1061
 
                if first >= dt and (last is None or dt >= last):
1062
 
                    return (i+1,)
1063
 
        else:
1064
 
            for i in range(len(revs)):
1065
 
                r = self.get_revision(revs[i])
1066
 
                # TODO: Handle timezone.
1067
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1068
 
                if first <= dt and (last is None or dt <= last):
1069
 
                    return (i+1,)
1070
 
    REVISION_NAMESPACES['date:'] = _namespace_date
1071
 
 
1072
1095
    def revision_tree(self, revision_id):
1073
1096
        """Return Tree for a revision on this branch.
1074
1097
 
1358
1381
        
1359
1382
 
1360
1383
 
1361
 
class ScratchBranch(Branch):
 
1384
class ScratchBranch(LocalBranch):
1362
1385
    """Special test class: a branch that cleans up after itself.
1363
1386
 
1364
1387
    >>> b = ScratchBranch()
1381
1404
        if base is None:
1382
1405
            base = mkdtemp()
1383
1406
            init = True
1384
 
        Branch.__init__(self, base, init=init)
 
1407
        LocalBranch.__init__(self, base, init=init)
1385
1408
        for d in dirs:
1386
1409
            os.mkdir(self.abspath(d))
1387
1410