1
# Copyright (C) 2005 Canonical Ltd
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.
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.
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
20
from bzrlib.errors import BzrError, NoSuchRevision
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 = {}
27
def get_revision_info(branch, spec):
28
"""Return (revno, revision id) for revision specifier.
30
spec can be an integer, in which case it is assumed to be revno
31
(though this will translate negative values into positive ones)
32
spec can also be a string, in which case it is parsed for something
33
like 'date:' or 'revid:' etc.
35
A revid is always returned. If it is None, the specifier referred to
36
the null revision. If the revid does not occur in the revision
37
history, revno will be None.
43
try:# Convert to int if possible
47
revs = branch.revision_history()
48
if isinstance(spec, int):
50
revno = len(revs) + spec + 1
53
rev_id = branch.get_rev_id(revno, revs)
54
elif isinstance(spec, basestring):
55
for prefix, func in REVISION_NAMESPACES.iteritems():
56
if spec.startswith(prefix):
57
result = func(branch, revs, spec)
59
revno, rev_id = result
62
rev_id = branch.get_rev_id(revno, revs)
65
raise BzrError('No namespace registered for string: %r' %
68
raise TypeError('Unhandled revision type %s' % spec)
70
if revno is None or rev_id is None:
71
raise NoSuchRevision(branch, spec)
77
def _namespace_revno(branch, revs, spec):
78
"""Lookup a revision by revision number"""
79
assert spec.startswith('revno:')
81
return (int(spec[len('revno:'):]),)
84
REVISION_NAMESPACES['revno:'] = _namespace_revno
87
def _namespace_revid(branch, revs, spec):
88
assert spec.startswith('revid:')
89
rev_id = spec[len('revid:'):]
91
return revs.index(rev_id) + 1, rev_id
94
REVISION_NAMESPACES['revid:'] = _namespace_revid
97
def _namespace_last(branch, revs, spec):
98
assert spec.startswith('last:')
100
offset = int(spec[5:])
105
raise BzrError('You must supply a positive value for --revision last:XXX')
106
return (len(revs) - offset + 1,)
107
REVISION_NAMESPACES['last:'] = _namespace_last
110
def _namespace_tag(branch, revs, spec):
111
assert spec.startswith('tag:')
112
raise BzrError('tag: namespace registered, but not implemented.')
113
REVISION_NAMESPACES['tag:'] = _namespace_tag
116
_date_re = re.compile(
117
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
119
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
122
def _namespace_date(branch, revs, spec):
124
Spec for date revisions:
126
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
127
it can also start with a '+/-/='. '+' says match the first
128
entry after the given date. '-' is match the first entry before the date
129
'=' is match the first entry after, but still on the given date.
131
+2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
132
-2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
133
=2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
134
May 13th, 2005 at 0:00
136
So the proper way of saying 'give me all entries for today' is:
137
-r {date:+today}:{date:-tomorrow}
138
The default is '=' when not supplied
140
assert spec.startswith('date:')
143
if val[:1] in ('+', '-', '='):
144
match_style = val[:1]
147
# XXX: this should probably be using datetime.date instead
148
today = datetime.datetime.today().replace(hour=0, minute=0, second=0,
150
if val.lower() == 'yesterday':
151
dt = today - datetime.timedelta(days=1)
152
elif val.lower() == 'today':
154
elif val.lower() == 'tomorrow':
155
dt = today + datetime.timedelta(days=1)
157
m = _date_re.match(val)
158
if not m or (not m.group('date') and not m.group('time')):
159
raise BzrError('Invalid revision date %r' % spec)
162
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
164
year, month, day = today.year, today.month, today.day
166
hour = int(m.group('hour'))
167
minute = int(m.group('minute'))
168
if m.group('second'):
169
second = int(m.group('second'))
173
hour, minute, second = 0,0,0
175
dt = datetime.datetime(year=year, month=month, day=day,
176
hour=hour, minute=minute, second=second)
180
if match_style == '-':
182
elif match_style == '=':
183
last = dt + datetime.timedelta(days=1)
186
for i in range(len(revs)-1, -1, -1):
187
r = branch.get_revision(revs[i])
188
# TODO: Handle timezone.
189
dt = datetime.datetime.fromtimestamp(r.timestamp)
190
if first >= dt and (last is None or dt >= last):
193
for i in range(len(revs)):
194
r = branch.get_revision(revs[i])
195
# TODO: Handle timezone.
196
dt = datetime.datetime.fromtimestamp(r.timestamp)
197
if first <= dt and (last is None or dt <= last):
199
REVISION_NAMESPACES['date:'] = _namespace_date