Merge lp:~jelmer/brz/fix-datetime into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/fix-datetime
Merge into: lp:brz
Diff against target: 253 lines (+111/-68)
3 files modified
breezy/revision.py (+5/-0)
breezy/revisionspec.py (+81/-59)
breezy/tests/test_revisionspec.py (+25/-9)
To merge this branch: bzr merge lp:~jelmer/brz/fix-datetime
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+426105@code.launchpad.net

Commit message

Fix date: in git repositories.

Description of the change

Fix date: in git repositories.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/revision.py'
2--- breezy/revision.py 2021-07-04 18:04:19 +0000
3+++ breezy/revision.py 2022-07-03 21:23:15 +0000
4@@ -75,6 +75,11 @@
5 def __repr__(self):
6 return "<Revision id %s>" % self.revision_id
7
8+ def datetime(self):
9+ import datetime
10+ # TODO: Handle timezone.
11+ return datetime.datetime.fromtimestamp(self.timestamp)
12+
13 def __eq__(self, other):
14 if not isinstance(other, Revision):
15 return False
16
17=== modified file 'breezy/revisionspec.py'
18--- breezy/revisionspec.py 2022-07-02 11:03:41 +0000
19+++ breezy/revisionspec.py 2022-07-03 21:23:15 +0000
20@@ -25,6 +25,7 @@
21 errors,
22 lazy_regex,
23 registry,
24+ revision as _mod_revision,
25 trace,
26 )
27
28@@ -641,13 +642,56 @@
29
30 def __getitem__(self, index):
31 """Get the date of the index'd item"""
32- import datetime
33 r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
34- # TODO: Handle timezone.
35- return datetime.datetime.fromtimestamp(r.timestamp)
36-
37- def __len__(self):
38- return self.branch.revno()
39+ return r.datetime()
40+
41+
42+_date_regex = lazy_regex.lazy_compile(
43+ r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
44+ r'(,|T)?\s*'
45+ r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
46+ )
47+
48+
49+def _parse_datespec(spec):
50+ import datetime
51+ # XXX: This doesn't actually work
52+ # So the proper way of saying 'give me all entries for today' is:
53+ # -r date:yesterday..date:today
54+ today = datetime.datetime.fromordinal(
55+ datetime.date.today().toordinal())
56+ if spec.lower() == 'yesterday':
57+ return today - datetime.timedelta(days=1)
58+ elif spec.lower() == 'today':
59+ return today
60+ elif spec.lower() == 'tomorrow':
61+ return today + datetime.timedelta(days=1)
62+ else:
63+ m = _date_regex.match(spec)
64+ if not m or (not m.group('date') and not m.group('time')):
65+ raise ValueError
66+
67+ if m.group('date'):
68+ year = int(m.group('year'))
69+ month = int(m.group('month'))
70+ day = int(m.group('day'))
71+ else:
72+ year = today.year
73+ month = today.month
74+ day = today.day
75+
76+ if m.group('time'):
77+ hour = int(m.group('hour'))
78+ minute = int(m.group('minute'))
79+ if m.group('second'):
80+ second = int(m.group('second'))
81+ else:
82+ second = 0
83+ else:
84+ hour, minute, second = 0, 0, 0
85+
86+ return datetime.datetime(year=year, month=month, day=day,
87+ hour=hour, minute=minute, second=second)
88
89
90 class RevisionSpec_date(RevisionSpec):
91@@ -671,11 +715,28 @@
92 August 14th, 2006 at 5:10pm.
93 """
94 prefix = 'date:'
95- _date_regex = lazy_regex.lazy_compile(
96- r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
97- r'(,|T)?\s*'
98- r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
99- )
100+
101+ def _scan_backwards(self, branch, dt):
102+ with branch.lock_read():
103+ graph = branch.repository.get_graph()
104+ last_match = None
105+ for revid in graph.iter_lefthand_ancestry(
106+ branch.last_revision(), (_mod_revision.NULL_REVISION,)):
107+ r = branch.repository.get_revision(revid)
108+ if r.datetime() < dt:
109+ if last_match is None:
110+ raise InvalidRevisionSpec(self.user_spec, branch)
111+ return RevisionInfo(branch, None, last_match)
112+ last_match = revid
113+ return RevisionInfo(branch, None, last_match)
114+
115+ def _bisect_backwards(self, branch, dt, hi):
116+ import bisect
117+ with branch.lock_read():
118+ rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1, hi)
119+ if rev == branch.revno():
120+ raise InvalidRevisionSpec(self.user_spec, branch)
121+ return RevisionInfo(branch, rev)
122
123 def _match_on(self, branch, revs):
124 """Spec for date revisions:
125@@ -684,55 +745,16 @@
126 matches the first entry after a given date (either at midnight or
127 at a specified time).
128 """
129- import bisect
130- import datetime
131- # XXX: This doesn't actually work
132- # So the proper way of saying 'give me all entries for today' is:
133- # -r date:yesterday..date:today
134- today = datetime.datetime.fromordinal(
135- datetime.date.today().toordinal())
136- if self.spec.lower() == 'yesterday':
137- dt = today - datetime.timedelta(days=1)
138- elif self.spec.lower() == 'today':
139- dt = today
140- elif self.spec.lower() == 'tomorrow':
141- dt = today + datetime.timedelta(days=1)
142+ try:
143+ dt = _parse_datespec(self.spec)
144+ except ValueError:
145+ raise InvalidRevisionSpec(
146+ self.user_spec, branch, 'invalid date')
147+ revno = branch.revno()
148+ if revno is None:
149+ return self._scan_backwards(branch, dt)
150 else:
151- m = self._date_regex.match(self.spec)
152- if not m or (not m.group('date') and not m.group('time')):
153- raise InvalidRevisionSpec(
154- self.user_spec, branch, 'invalid date')
155-
156- try:
157- if m.group('date'):
158- year = int(m.group('year'))
159- month = int(m.group('month'))
160- day = int(m.group('day'))
161- else:
162- year = today.year
163- month = today.month
164- day = today.day
165-
166- if m.group('time'):
167- hour = int(m.group('hour'))
168- minute = int(m.group('minute'))
169- if m.group('second'):
170- second = int(m.group('second'))
171- else:
172- second = 0
173- else:
174- hour, minute, second = 0, 0, 0
175- except ValueError:
176- raise InvalidRevisionSpec(
177- self.user_spec, branch, 'invalid date')
178-
179- dt = datetime.datetime(year=year, month=month, day=day,
180- hour=hour, minute=minute, second=second)
181- with branch.lock_read():
182- rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
183- if rev == branch.revno():
184- raise InvalidRevisionSpec(self.user_spec, branch)
185- return RevisionInfo(branch, rev)
186+ return self._bisect_backwards(branch, dt, revno)
187
188
189 class RevisionSpec_ancestor(RevisionSpec):
190
191=== modified file 'breezy/tests/test_revisionspec.py'
192--- breezy/tests/test_revisionspec.py 2020-07-28 02:11:05 +0000
193+++ breezy/tests/test_revisionspec.py 2022-07-03 21:23:15 +0000
194@@ -517,10 +517,10 @@
195 super(TestRevisionSpec, self).setUp()
196
197 new_tree = self.make_branch_and_tree('new_tree')
198- new_tree.commit('Commit one', rev_id=b'new_r1',
199- timestamp=time.time() - 60 * 60 * 24)
200- new_tree.commit('Commit two', rev_id=b'new_r2')
201- new_tree.commit('Commit three', rev_id=b'new_r3')
202+ self.revid1 = new_tree.commit(
203+ 'Commit one', timestamp=time.time() - 60 * 60 * 24)
204+ self.revid2 = new_tree.commit('Commit two')
205+ self.revid3 = new_tree.commit('Commit three')
206
207 self.tree = new_tree
208
209@@ -528,11 +528,11 @@
210 self.assertInvalid('date:tomorrow')
211
212 def test_today(self):
213- self.assertInHistoryIs(2, b'new_r2', 'date:today')
214- self.assertInHistoryIs(1, b'new_r1', 'before:date:today')
215+ self.assertInHistoryIs(2, self.revid2, 'date:today')
216+ self.assertInHistoryIs(1, self.revid1, 'before:date:today')
217
218 def test_yesterday(self):
219- self.assertInHistoryIs(1, b'new_r1', 'date:yesterday')
220+ self.assertInHistoryIs(1, self.revid1, 'date:yesterday')
221
222 def test_invalid(self):
223 self.assertInvalid('date:foobar', extra='\ninvalid date')
224@@ -543,11 +543,27 @@
225
226 def test_day(self):
227 now = datetime.datetime.now()
228- self.assertInHistoryIs(2, b'new_r2',
229+ self.assertInHistoryIs(2, self.revid2,
230 'date:%04d-%02d-%02d' % (now.year, now.month, now.day))
231
232 def test_as_revision_id(self):
233- self.assertAsRevisionId(b'new_r2', 'date:today')
234+ self.assertAsRevisionId(self.revid2, 'date:today')
235+
236+
237+class TestRevisionSpec_date_no_revno(TestRevisionSpec_date):
238+
239+ # some formats don't implement .revno(), so it triggers a different codepath
240+
241+ def get_in_history(self, revision_spec):
242+ old_revno = self.overrideAttr(self.tree.branch, 'revno', lambda: None)
243+ try:
244+ return spec_in_history(revision_spec, self.tree.branch)
245+ finally:
246+ self.tree.branch.revno = old_revno
247+
248+ def test_today(self):
249+ self.assertInHistoryIs(2, self.revid2, 'date:today')
250+ # Drop before: since it messes with our monkeypatching of Branch.revno.
251
252
253 class TestRevisionSpec_ancestor(TestRevisionSpec):

Subscribers

People subscribed via source and target branches