/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1185.2.5 by Lalo Martins
moving the 'revision spec' stuff out of the Branch class and into a new
1
# Copyright (C) 2005 Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
import datetime
19
import re
20
from bzrlib.errors import BzrError, NoSuchRevision
21
22
# Map some sort of prefix into a namespace
23
# stuff like "revno:10", "revid:", etc.
24
# This should match a prefix with a function which accepts it
25
REVISION_NAMESPACES = {}
26
1185.2.6 by Lalo Martins
turned get_revision_info into a RevisionSpec class
27
class RevisionSpec(object):
28
    """Equivalent to the old get_revision_info().
29
    An instance has two useful attributes: revno, and rev_id.
30
31
    They can also be accessed as spec[0] and spec[1] respectively,
32
    so that you can write code like:
33
    revno, rev_id = RevisionSpec(branch, spec)
34
    although this is probably going to be deprecated later.
35
36
    Revision specs are an UI element, and they have been moved out
37
    of the branch class to leave "back-end" classes unaware of such
38
    details.  Code that gets a revno or rev_id from other code should
39
    not be using revision specs - revnos and revision ids are the
40
    accepted ways to refer to revisions internally.
1185.2.5 by Lalo Martins
moving the 'revision spec' stuff out of the Branch class and into a new
41
    """
1185.2.6 by Lalo Martins
turned get_revision_info into a RevisionSpec class
42
    def __init__(self, branch, spec):
43
        """Parse a revision specifier.
44
45
        spec can be an integer, in which case it is assumed to be revno
46
        (though this will translate negative values into positive ones)
47
        spec can also be a string, in which case it is parsed for something
48
        like 'date:' or 'revid:' etc.
49
        """
50
        self.branch = branch
51
52
        if spec is None:
53
            self.revno = 0
54
            self.rev_id = None
55
            return
56
        self.revno = None
57
        try:# Convert to int if possible
58
            spec = int(spec)
59
        except ValueError:
60
            pass
61
        revs = branch.revision_history()
62
        if isinstance(spec, int):
63
            if spec < 0:
64
                self.revno = len(revs) + spec + 1
65
            else:
66
                self.revno = spec
67
            self.rev_id = branch.get_rev_id(self.revno, revs)
68
        elif isinstance(spec, basestring):
69
            for prefix, func in REVISION_NAMESPACES.iteritems():
70
                if spec.startswith(prefix):
71
                    result = func(branch, revs, spec)
72
                    if len(result) > 1:
73
                        self.revno, self.rev_id = result
74
                    else:
75
                        self.revno = result[0]
76
                        self.rev_id = branch.get_rev_id(self.revno, revs)
77
                    break
78
            else:
79
                raise BzrError('No namespace registered for string: %r' %
80
                               spec)
81
        else:
82
            raise TypeError('Unhandled revision type %s' % spec)
83
84
        if self.revno is None or self.rev_id is None:
85
            raise NoSuchRevision(branch, spec)
86
87
    def __len__(self):
88
        return 2
89
90
    def __getitem__(self, index):
91
        if index == 0: return self.revno
92
        if index == 1: return self.rev_id
93
        raise IndexError(index)
94
95
    def get(self):
96
        return self.branch.get_revision(self.rev_id)
97
98
    def __eq__(self, other):
99
        if type(other) not in (tuple, list, type(self)):
100
            return False
101
        if type(other) is type(self) and self.branch is not other.branch:
102
            return False
103
        print 'comparing', tuple(self), tuple(other)
104
        return tuple(self) == tuple(other)
105
106
    def __repr__(self):
107
        return '<bzrlib.revisionspec.RevisionSpec object %s, %s for %r>' % (
108
            self.revno, self.rev_id, self.branch)
1185.2.5 by Lalo Martins
moving the 'revision spec' stuff out of the Branch class and into a new
109
110
111
# private API
112
113
def _namespace_revno(branch, revs, spec):
114
    """Lookup a revision by revision number"""
115
    assert spec.startswith('revno:')
116
    try:
117
        return (int(spec[len('revno:'):]),)
118
    except ValueError:
119
        return (None,)
120
REVISION_NAMESPACES['revno:'] = _namespace_revno
121
122
123
def _namespace_revid(branch, revs, spec):
124
    assert spec.startswith('revid:')
125
    rev_id = spec[len('revid:'):]
126
    try:
127
        return revs.index(rev_id) + 1, rev_id
128
    except ValueError:
129
        return (None,)
130
REVISION_NAMESPACES['revid:'] = _namespace_revid
131
132
133
def _namespace_last(branch, revs, spec):
134
    assert spec.startswith('last:')
135
    try:
136
        offset = int(spec[5:])
137
    except ValueError:
138
        return (None,)
139
    else:
140
        if offset <= 0:
141
            raise BzrError('You must supply a positive value for --revision last:XXX')
142
        return (len(revs) - offset + 1,)
143
REVISION_NAMESPACES['last:'] = _namespace_last
144
145
146
def _namespace_tag(branch, revs, spec):
147
    assert spec.startswith('tag:')
148
    raise BzrError('tag: namespace registered, but not implemented.')
149
REVISION_NAMESPACES['tag:'] = _namespace_tag
150
151
152
_date_re = re.compile(
153
        r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
154
        r'(,|T)?\s*'
155
        r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
156
    )
157
158
def _namespace_date(branch, revs, spec):
159
    """
160
    Spec for date revisions:
161
      date:value
162
      value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
163
      it can also start with a '+/-/='. '+' says match the first
164
      entry after the given date. '-' is match the first entry before the date
165
      '=' is match the first entry after, but still on the given date.
166
    
167
      +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
168
      -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
169
      =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
170
          May 13th, 2005 at 0:00
171
    
172
      So the proper way of saying 'give me all entries for today' is:
173
          -r {date:+today}:{date:-tomorrow}
174
      The default is '=' when not supplied
175
    """
176
    assert spec.startswith('date:')
177
    val = spec[5:]
178
    match_style = '='
179
    if val[:1] in ('+', '-', '='):
180
        match_style = val[:1]
181
        val = val[1:]
182
183
    # XXX: this should probably be using datetime.date instead
184
    today = datetime.datetime.today().replace(hour=0, minute=0, second=0,
185
                                              microsecond=0)
186
    if val.lower() == 'yesterday':
187
        dt = today - datetime.timedelta(days=1)
188
    elif val.lower() == 'today':
189
        dt = today
190
    elif val.lower() == 'tomorrow':
191
        dt = today + datetime.timedelta(days=1)
192
    else:
193
        m = _date_re.match(val)
194
        if not m or (not m.group('date') and not m.group('time')):
195
            raise BzrError('Invalid revision date %r' % spec)
196
197
        if m.group('date'):
198
            year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
199
        else:
200
            year, month, day = today.year, today.month, today.day
201
        if m.group('time'):
202
            hour = int(m.group('hour'))
203
            minute = int(m.group('minute'))
204
            if m.group('second'):
205
                second = int(m.group('second'))
206
            else:
207
                second = 0
208
        else:
209
            hour, minute, second = 0,0,0
210
211
        dt = datetime.datetime(year=year, month=month, day=day,
212
                hour=hour, minute=minute, second=second)
213
    first = dt
214
    last = None
215
    reversed = False
216
    if match_style == '-':
217
        reversed = True
218
    elif match_style == '=':
219
        last = dt + datetime.timedelta(days=1)
220
221
    if reversed:
222
        for i in range(len(revs)-1, -1, -1):
223
            r = branch.get_revision(revs[i])
224
            # TODO: Handle timezone.
225
            dt = datetime.datetime.fromtimestamp(r.timestamp)
226
            if first >= dt and (last is None or dt >= last):
227
                return (i+1,)
228
    else:
229
        for i in range(len(revs)):
230
            r = branch.get_revision(revs[i])
231
            # TODO: Handle timezone.
232
            dt = datetime.datetime.fromtimestamp(r.timestamp)
233
            if first <= dt and (last is None or dt <= last):
234
                return (i+1,)
235
REVISION_NAMESPACES['date:'] = _namespace_date