~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

merge merge tweaks from aaron, which includes latest .dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import datetime
19
19
import re
20
 
import bisect
21
20
from bzrlib.errors import BzrError, NoSuchRevision, NoCommits
22
21
 
23
22
_marker = []
58
57
        # TODO: otherwise, it should depend on how I was built -
59
58
        # if it's in_history(branch), then check revision_history(),
60
59
        # if it's in_store(branch), do the check below
61
 
        return self.branch.repository.has_revision(self.rev_id)
 
60
        return self.rev_id in self.branch.revision_store
62
61
 
63
62
    def __len__(self):
64
63
        return 2
69
68
        raise IndexError(index)
70
69
 
71
70
    def get(self):
72
 
        return self.branch.repository.get_revision(self.rev_id)
 
71
        return self.branch.get_revision(self.rev_id)
73
72
 
74
73
    def __eq__(self, other):
75
74
        if type(other) not in (tuple, list, type(self)):
152
151
        revs = branch.revision_history()
153
152
        return self._match_on_and_check(branch, revs)
154
153
 
155
 
        # FIXME: in_history is somewhat broken,
156
 
        # it will return non-history revisions in many
157
 
        # circumstances. The expected facility is that
158
 
        # in_history only returns revision-history revs,
159
 
        # in_store returns any rev. RBC 20051010
160
 
    # aliases for now, when we fix the core logic, then they
161
 
    # will do what you expect.
162
 
    in_store = in_history
163
 
    in_branch = in_store
164
 
        
165
154
    def __repr__(self):
166
155
        # this is mostly for helping with testing
167
156
        return '<%s %s%s>' % (self.__class__.__name__,
205
194
        try:
206
195
            return RevisionInfo(branch, revs.index(self.spec) + 1, self.spec)
207
196
        except ValueError:
208
 
            return RevisionInfo(branch, None, self.spec)
 
197
            return RevisionInfo(branch, None)
209
198
 
210
199
SPEC_TYPES.append(RevisionSpec_revid)
211
200
 
212
201
 
213
202
class RevisionSpec_last(RevisionSpec):
214
 
 
215
203
    prefix = 'last:'
216
204
 
217
205
    def _match_on(self, branch, revs):
227
215
SPEC_TYPES.append(RevisionSpec_last)
228
216
 
229
217
 
230
 
class RevisionSpec_before(RevisionSpec):
231
 
 
232
 
    prefix = 'before:'
233
 
    
234
 
    def _match_on(self, branch, revs):
235
 
        r = RevisionSpec(self.spec)._match_on(branch, revs)
236
 
        if (r.revno is None) or (r.revno == 0):
237
 
            return r
238
 
        return RevisionInfo(branch, r.revno - 1)
239
 
 
240
 
SPEC_TYPES.append(RevisionSpec_before)
241
 
 
242
 
 
243
218
class RevisionSpec_tag(RevisionSpec):
244
219
    prefix = 'tag:'
245
220
 
249
224
SPEC_TYPES.append(RevisionSpec_tag)
250
225
 
251
226
 
252
 
class RevisionSpec_revs:
253
 
    def __init__(self, revs, branch):
254
 
        self.revs = revs
255
 
        self.branch = branch
256
 
    def __getitem__(self, index):
257
 
        r = self.branch.repository.get_revision(self.revs[index])
258
 
        # TODO: Handle timezone.
259
 
        return datetime.datetime.fromtimestamp(r.timestamp)
260
 
    def __len__(self):
261
 
        return len(self.revs)
262
 
 
263
 
 
264
 
class RevisionSpec_revs:
265
 
    def __init__(self, revs, branch):
266
 
        self.revs = revs
267
 
        self.branch = branch
268
 
    def __getitem__(self, index):
269
 
        r = self.branch.repository.get_revision(self.revs[index])
270
 
        # TODO: Handle timezone.
271
 
        return datetime.datetime.fromtimestamp(r.timestamp)
272
 
    def __len__(self):
273
 
        return len(self.revs)
274
 
 
275
 
 
276
227
class RevisionSpec_date(RevisionSpec):
277
228
    prefix = 'date:'
278
229
    _date_re = re.compile(
286
237
        Spec for date revisions:
287
238
          date:value
288
239
          value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
289
 
          matches the first entry after a given date (either at midnight or
290
 
          at a specified time).
 
240
          it can also start with a '+/-/='. '+' says match the first
 
241
          entry after the given date. '-' is match the first entry before the date
 
242
          '=' is match the first entry after, but still on the given date.
 
243
 
 
244
          +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
 
245
          -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
 
246
          =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
 
247
              May 13th, 2005 at 0:00
291
248
 
292
249
          So the proper way of saying 'give me all entries for today' is:
293
 
              -r date:today..date:tomorrow
 
250
              -r {date:+today}:{date:-tomorrow}
 
251
          The default is '=' when not supplied
294
252
        """
295
 
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
 
253
        match_style = '='
 
254
        if self.spec[:1] in ('+', '-', '='):
 
255
            match_style = self.spec[:1]
 
256
            self.spec = self.spec[1:]
 
257
 
 
258
        # XXX: this should probably be using datetime.date instead
 
259
        today = datetime.datetime.today().replace(hour=0, minute=0, second=0,
 
260
                                                  microsecond=0)
296
261
        if self.spec.lower() == 'yesterday':
297
262
            dt = today - datetime.timedelta(days=1)
298
263
        elif self.spec.lower() == 'today':
320
285
 
321
286
            dt = datetime.datetime(year=year, month=month, day=day,
322
287
                    hour=hour, minute=minute, second=second)
323
 
        branch.lock_read()
324
 
        try:
325
 
            rev = bisect.bisect(RevisionSpec_revs(revs, branch), dt)
326
 
        finally:
327
 
            branch.unlock()
328
 
        if rev == len(revs):
329
 
            return RevisionInfo(branch, None)
 
288
        first = dt
 
289
        last = None
 
290
        reversed = False
 
291
        if match_style == '-':
 
292
            reversed = True
 
293
        elif match_style == '=':
 
294
            last = dt + datetime.timedelta(days=1)
 
295
 
 
296
        if reversed:
 
297
            for i in range(len(revs)-1, -1, -1):
 
298
                r = branch.get_revision(revs[i])
 
299
                # TODO: Handle timezone.
 
300
                dt = datetime.datetime.fromtimestamp(r.timestamp)
 
301
                if first >= dt and (last is None or dt >= last):
 
302
                    return RevisionInfo(branch, i+1,)
330
303
        else:
331
 
            return RevisionInfo(branch, rev + 1)
 
304
            for i in range(len(revs)):
 
305
                r = branch.get_revision(revs[i])
 
306
                # TODO: Handle timezone.
 
307
                dt = datetime.datetime.fromtimestamp(r.timestamp)
 
308
                if first <= dt and (last is None or dt <= last):
 
309
                    return RevisionInfo(branch, i+1,)
332
310
 
333
311
SPEC_TYPES.append(RevisionSpec_date)
334
312
 
339
317
    def _match_on(self, branch, revs):
340
318
        from branch import Branch
341
319
        from revision import common_ancestor, MultipleRevisionSources
342
 
        other_branch = Branch.open_containing(self.spec)[0]
343
 
        revision_a = branch.last_revision()
344
 
        revision_b = other_branch.last_revision()
 
320
        other_branch = Branch.open_containing(self.spec)
 
321
        revision_a = branch.last_patch()
 
322
        revision_b = other_branch.last_patch()
345
323
        for r, b in ((revision_a, branch), (revision_b, other_branch)):
346
324
            if r is None:
347
325
                raise NoCommits(b)
348
 
        revision_source = MultipleRevisionSources(branch.repository,
349
 
                                                  other_branch.repository)
 
326
        revision_source = MultipleRevisionSources(branch, other_branch)
350
327
        rev_id = common_ancestor(revision_a, revision_b, revision_source)
351
328
        try:
352
329
            revno = branch.revision_id_to_revno(rev_id)
355
332
        return RevisionInfo(branch, revno, rev_id)
356
333
        
357
334
SPEC_TYPES.append(RevisionSpec_ancestor)
358
 
 
359
 
class RevisionSpec_branch(RevisionSpec):
360
 
    """A branch: revision specifier.
361
 
 
362
 
    This takes the path to a branch and returns its tip revision id.
363
 
    """
364
 
    prefix = 'branch:'
365
 
 
366
 
    def _match_on(self, branch, revs):
367
 
        from branch import Branch
368
 
        other_branch = Branch.open_containing(self.spec)[0]
369
 
        revision_b = other_branch.last_revision()
370
 
        if revision_b is None:
371
 
            raise NoCommits(other_branch)
372
 
        # pull in the remote revisions so we can diff
373
 
        branch.fetch(other_branch, revision_b)
374
 
        try:
375
 
            revno = branch.revision_id_to_revno(revision_b)
376
 
        except NoSuchRevision:
377
 
            revno = None
378
 
        return RevisionInfo(branch, revno, revision_b)
379
 
        
380
 
SPEC_TYPES.append(RevisionSpec_branch)