143
143
Base directory/url of the branch.
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 = {}
152
147
def __new__(cls, *a, **kw):
153
148
"""this is temporary, till we get rid of all code that does
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)
162
def _namespace_revno(self, revs, revision):
163
"""Lookup a revision by revision number"""
164
assert revision.startswith('revno:')
166
return (int(revision[6:]),)
169
REVISION_NAMESPACES['revno:'] = _namespace_revno
171
def _namespace_revid(self, revs, revision):
172
assert revision.startswith('revid:')
173
rev_id = revision[len('revid:'):]
175
return revs.index(rev_id) + 1, rev_id
178
REVISION_NAMESPACES['revid:'] = _namespace_revid
180
def _namespace_last(self, revs, revision):
181
assert revision.startswith('last:')
183
offset = int(revision[5:])
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
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
197
def _namespace_date(self, revs, revision):
198
assert revision.startswith('date:')
200
# Spec for date revisions:
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.
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
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
217
if val[:1] in ('+', '-', '='):
218
match_style = val[:1]
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':
226
elif val.lower() == 'tomorrow':
227
dt = today + datetime.timedelta(days=1)
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))?'
234
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
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)
241
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
243
year, month, day = today.year, today.month, today.day
245
hour = int(m.group('hour'))
246
minute = int(m.group('minute'))
247
if m.group('second'):
248
second = int(m.group('second'))
252
hour, minute, second = 0,0,0
254
dt = datetime.datetime(year=year, month=month, day=day,
255
hour=hour, minute=minute, second=second)
259
if match_style == '-':
261
elif match_style == '=':
262
last = dt + datetime.timedelta(days=1)
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):
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):
278
REVISION_NAMESPACES['date:'] = _namespace_date
281
158
class LocalBranch(Branch):
282
159
"""A branch stored in the actual filesystem.
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
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
902
revno, info = get_revision_info(self, revision)
903
except NoSuchRevision:
1044
924
raise bzrlib.errors.NoSuchRevision(self, revno)
1045
925
return history[revno - 1]
1047
def _get_revision_info(self, revision):
1048
"""Return (revno, revision id) for revision specifier.
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.
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.
1060
if revision is None:
1063
try:# Convert to int if possible
1064
revision = int(revision)
1067
revs = self.revision_history()
1068
if isinstance(revision, int):
1070
revno = len(revs) + revision + 1
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)
1079
revno, rev_id = result
1082
rev_id = self.get_rev_id(revno, revs)
1085
raise BzrError('No namespace registered for string: %r' %
1088
raise TypeError('Unhandled revision type %s' % revision)
1092
raise bzrlib.errors.NoSuchRevision(self, revision)
1093
return revno, rev_id
1095
927
def revision_tree(self, revision_id):
1096
928
"""Return Tree for a revision on this branch.