140
140
"""Branch holding a history of revisions.
143
Base directory of the branch.
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
def __new__(cls, *a, **kw):
153
"""this is temporary, till we get rid of all code that does
156
# AAARGH! MY EYES! UUUUGLY!!!
159
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
class LocalBranch(Branch):
282
"""A branch stored in the actual filesystem.
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.
146
289
None, or 'r' or 'w'
951
1092
raise bzrlib.errors.NoSuchRevision(self, revision)
952
1093
return revno, rev_id
954
def _namespace_revno(self, revs, revision):
955
"""Lookup a revision by revision number"""
956
assert revision.startswith('revno:')
958
return (int(revision[6:]),)
961
REVISION_NAMESPACES['revno:'] = _namespace_revno
963
def _namespace_revid(self, revs, revision):
964
assert revision.startswith('revid:')
965
rev_id = revision[len('revid:'):]
967
return revs.index(rev_id) + 1, rev_id
970
REVISION_NAMESPACES['revid:'] = _namespace_revid
972
def _namespace_last(self, revs, revision):
973
assert revision.startswith('last:')
975
offset = int(revision[5:])
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
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
989
def _namespace_date(self, revs, revision):
990
assert revision.startswith('date:')
992
# Spec for date revisions:
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.
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
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
1009
if val[:1] in ('+', '-', '='):
1010
match_style = val[:1]
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':
1018
elif val.lower() == 'tomorrow':
1019
dt = today + datetime.timedelta(days=1)
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))?'
1026
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
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)
1033
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
1035
year, month, day = today.year, today.month, today.day
1037
hour = int(m.group('hour'))
1038
minute = int(m.group('minute'))
1039
if m.group('second'):
1040
second = int(m.group('second'))
1044
hour, minute, second = 0,0,0
1046
dt = datetime.datetime(year=year, month=month, day=day,
1047
hour=hour, minute=minute, second=second)
1051
if match_style == '-':
1053
elif match_style == '=':
1054
last = dt + datetime.timedelta(days=1)
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):
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):
1070
REVISION_NAMESPACES['date:'] = _namespace_date
1072
1095
def revision_tree(self, revision_id):
1073
1096
"""Return Tree for a revision on this branch.