~bzr-pqm/bzr/bzr.dev

1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
1
# Copyright (C) 2005 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
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
16
17
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
18
import bisect
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
19
import datetime
20
import re
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
21
22
from bzrlib import (
23
    errors,
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
24
    revision,
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
25
    symbol_versioning,
26
    trace,
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
27
    )
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
28
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
29
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
30
_marker = []
31
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
32
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
33
class RevisionInfo(object):
34
    """The results of applying a revision specification to a branch.
35
36
    An instance has two useful attributes: revno, and rev_id.
37
38
    They can also be accessed as spec[0] and spec[1] respectively,
39
    so that you can write code like:
40
    revno, rev_id = RevisionSpec(branch, spec)
41
    although this is probably going to be deprecated later.
42
43
    This class exists mostly to be the return value of a RevisionSpec,
44
    so that you can access the member you're interested in (number or id)
45
    or treat the result as a tuple.
46
    """
47
48
    def __init__(self, branch, revno, rev_id=_marker):
49
        self.branch = branch
50
        self.revno = revno
51
        if rev_id is _marker:
52
            # allow caller to be lazy
53
            if self.revno is None:
54
                self.rev_id = None
55
            else:
56
                self.rev_id = branch.get_rev_id(self.revno)
57
        else:
58
            self.rev_id = rev_id
59
60
    def __nonzero__(self):
61
        # first the easy ones...
62
        if self.rev_id is None:
63
            return False
64
        if self.revno is not None:
65
            return True
66
        # TODO: otherwise, it should depend on how I was built -
67
        # if it's in_history(branch), then check revision_history(),
68
        # if it's in_store(branch), do the check below
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
69
        return self.branch.repository.has_revision(self.rev_id)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
70
71
    def __len__(self):
72
        return 2
73
74
    def __getitem__(self, index):
75
        if index == 0: return self.revno
76
        if index == 1: return self.rev_id
77
        raise IndexError(index)
78
79
    def get(self):
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
80
        return self.branch.repository.get_revision(self.rev_id)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
81
82
    def __eq__(self, other):
83
        if type(other) not in (tuple, list, type(self)):
84
            return False
85
        if type(other) is type(self) and self.branch is not other.branch:
86
            return False
87
        return tuple(self) == tuple(other)
88
89
    def __repr__(self):
90
        return '<bzrlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
91
            self.revno, self.rev_id, self.branch)
92
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
93
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
94
# classes in this list should have a "prefix" attribute, against which
95
# string specs are matched
96
SPEC_TYPES = []
1948.4.35 by John Arbash Meinel
Move the _revno_regex to a more logical location
97
_revno_regex = None
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
98
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
99
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
100
class RevisionSpec(object):
101
    """A parsed revision specification.
102
103
    A revision specification can be an integer, in which case it is
104
    assumed to be a revno (though this will translate negative values
105
    into positive ones); or it can be a string, in which case it is
106
    parsed for something like 'date:' or 'revid:' etc.
107
108
    Revision specs are an UI element, and they have been moved out
109
    of the branch class to leave "back-end" classes unaware of such
110
    details.  Code that gets a revno or rev_id from other code should
111
    not be using revision specs - revnos and revision ids are the
112
    accepted ways to refer to revisions internally.
113
114
    (Equivalent to the old Branch method get_revision_info())
115
    """
116
117
    prefix = None
118
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
119
    def __new__(cls, spec, _internal=False):
120
        if _internal:
121
            return object.__new__(cls, spec, _internal=_internal)
122
123
        symbol_versioning.warn('Creating a RevisionSpec directly has'
124
                               ' been deprecated in version 0.11. Use'
1948.4.33 by John Arbash Meinel
Switch from get_revision_spec() to RevisionSpec.from_string() (as advised by Martin)
125
                               ' RevisionSpec.from_string()'
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
126
                               ' instead.',
127
                               DeprecationWarning, stacklevel=2)
1948.4.33 by John Arbash Meinel
Switch from get_revision_spec() to RevisionSpec.from_string() (as advised by Martin)
128
        return RevisionSpec.from_string(spec)
129
130
    @staticmethod
131
    def from_string(spec):
132
        """Parse a revision spec string into a RevisionSpec object.
133
134
        :param spec: A string specified by the user
135
        :return: A RevisionSpec object that understands how to parse the
136
            supplied notation.
137
        """
138
        if not isinstance(spec, (type(None), basestring)):
139
            raise TypeError('error')
140
141
        if spec is None:
142
            return RevisionSpec(None, _internal=True)
143
144
        assert isinstance(spec, basestring), \
145
            "You should only supply strings not %s" % (type(spec),)
146
147
        for spectype in SPEC_TYPES:
148
            if spec.startswith(spectype.prefix):
149
                trace.mutter('Returning RevisionSpec %s for %s',
150
                             spectype.__name__, spec)
151
                return spectype(spec, _internal=True)
152
        else:
153
            # RevisionSpec_revno is special cased, because it is the only
154
            # one that directly handles plain integers
155
            global _revno_regex
156
            if _revno_regex is None:
157
                _revno_regex = re.compile(r'-?\d+(:.*)?$')
158
            if _revno_regex.match(spec) is not None:
159
                return RevisionSpec_revno(spec, _internal=True)
160
161
            raise errors.NoSuchRevisionSpec(spec)
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
162
163
    def __init__(self, spec, _internal=False):
164
        """Create a RevisionSpec referring to the Null revision.
165
166
        :param spec: The original spec supplied by the user
167
        :param _internal: Used to ensure that RevisionSpec is not being
1948.4.33 by John Arbash Meinel
Switch from get_revision_spec() to RevisionSpec.from_string() (as advised by Martin)
168
            called directly. Only from RevisionSpec.from_string()
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
169
        """
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
170
        if not _internal:
171
            # XXX: Update this after 0.10 is released
172
            symbol_versioning.warn('Creating a RevisionSpec directly has'
173
                                   ' been deprecated in version 0.11. Use'
1948.4.33 by John Arbash Meinel
Switch from get_revision_spec() to RevisionSpec.from_string() (as advised by Martin)
174
                                   ' RevisionSpec.from_string()'
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
175
                                   ' instead.',
176
                                   DeprecationWarning, stacklevel=2)
177
        self.user_spec = spec
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
178
        if self.prefix and spec.startswith(self.prefix):
179
            spec = spec[len(self.prefix):]
180
        self.spec = spec
181
182
    def _match_on(self, branch, revs):
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
183
        trace.mutter('Returning RevisionSpec._match_on: None')
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
184
        return RevisionInfo(branch, 0, None)
185
186
    def _match_on_and_check(self, branch, revs):
187
        info = self._match_on(branch, revs)
188
        if info:
189
            return info
190
        elif info == (0, None):
191
            # special case - the empty tree
192
            return info
193
        elif self.prefix:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
194
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
195
        else:
1948.4.2 by John Arbash Meinel
Update _match_on_and_check to raise the right error
196
            raise errors.InvalidRevisionSpec(self.spec, branch)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
197
198
    def in_history(self, branch):
1732.3.1 by Matthieu Moy
Implementation of -r revno:N:/path/to/branch
199
        if branch:
200
            revs = branch.revision_history()
201
        else:
202
            revs = None
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
203
        return self._match_on_and_check(branch, revs)
204
1432 by Robert Collins
branch: namespace
205
        # FIXME: in_history is somewhat broken,
206
        # it will return non-history revisions in many
207
        # circumstances. The expected facility is that
208
        # in_history only returns revision-history revs,
209
        # in_store returns any rev. RBC 20051010
210
    # aliases for now, when we fix the core logic, then they
211
    # will do what you expect.
212
    in_store = in_history
213
    in_branch = in_store
214
        
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
215
    def __repr__(self):
216
        # this is mostly for helping with testing
1948.4.32 by John Arbash Meinel
Clean up __repr__, as well as add tests that we can handle -r12:branch/
217
        return '<%s %s>' % (self.__class__.__name__,
218
                              self.user_spec)
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
219
    
1881.1.4 by Matthieu Moy
needs_tree -> needs_branch
220
    def needs_branch(self):
221
        """Whether this revision spec needs a branch.
222
1711.2.99 by John Arbash Meinel
minor typo fix
223
        Set this to False the branch argument of _match_on is not used.
224
        """
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
225
        return True
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
226
1948.4.22 by John Arbash Meinel
Refactor common code from integer revno handlers
227
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
228
# private API
229
230
class RevisionSpec_revno(RevisionSpec):
231
    prefix = 'revno:'
232
233
    def _match_on(self, branch, revs):
234
        """Lookup a revision by revision number"""
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
235
        loc = self.spec.find(':')
236
        if loc == -1:
237
            revno_spec = self.spec
238
            branch_spec = None
239
        else:
240
            revno_spec = self.spec[:loc]
241
            branch_spec = self.spec[loc+1:]
242
243
        if revno_spec == '':
1948.4.6 by John Arbash Meinel
A small bugfix, and more tests for revno:
244
            if not branch_spec:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
245
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.5 by John Arbash Meinel
Fix tests for negative entries, and add tests for revno:
246
                        branch, 'cannot have an empty revno and no branch')
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
247
            revno = None
248
        else:
249
            try:
250
                revno = int(revno_spec)
251
            except ValueError, e:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
252
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.5 by John Arbash Meinel
Fix tests for negative entries, and add tests for revno:
253
                                                 branch, e)
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
254
1948.4.6 by John Arbash Meinel
A small bugfix, and more tests for revno:
255
        if branch_spec:
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
256
            from bzrlib.branch import Branch
257
            branch = Branch.open(branch_spec)
1948.4.22 by John Arbash Meinel
Refactor common code from integer revno handlers
258
            # Need to use a new revision history
259
            # because we are using a specific branch
260
            revs = branch.revision_history()
261
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
262
        if revno < 0:
263
            if (-revno) >= len(revs):
264
                revno = 1
265
            else:
266
                revno = len(revs) + revno + 1
267
        try:
268
            revision_id = branch.get_rev_id(revno, revs)
269
        except errors.NoSuchRevision:
270
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
271
        return RevisionInfo(branch, revno, revision_id)
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
272
        
1881.1.4 by Matthieu Moy
needs_tree -> needs_branch
273
    def needs_branch(self):
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
274
        return self.spec.find(':') == -1
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
275
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
276
# Old compatibility 
277
RevisionSpec_int = RevisionSpec_revno
278
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
279
SPEC_TYPES.append(RevisionSpec_revno)
280
281
282
class RevisionSpec_revid(RevisionSpec):
283
    prefix = 'revid:'
284
285
    def _match_on(self, branch, revs):
286
        try:
1948.4.2 by John Arbash Meinel
Update _match_on_and_check to raise the right error
287
            revno = revs.index(self.spec) + 1
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
288
        except ValueError:
1948.4.2 by John Arbash Meinel
Update _match_on_and_check to raise the right error
289
            revno = None
290
        return RevisionInfo(branch, revno, self.spec)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
291
292
SPEC_TYPES.append(RevisionSpec_revid)
293
294
295
class RevisionSpec_last(RevisionSpec):
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
296
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
297
    prefix = 'last:'
298
299
    def _match_on(self, branch, revs):
1948.4.9 by John Arbash Meinel
Cleanup and test last:
300
        if self.spec == '':
301
            if not revs:
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
302
                raise errors.NoCommits(branch)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
303
            return RevisionInfo(branch, len(revs), revs[-1])
304
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
305
        try:
306
            offset = int(self.spec)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
307
        except ValueError, e:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
308
            raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
309
310
        if offset <= 0:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
311
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
1948.4.9 by John Arbash Meinel
Cleanup and test last:
312
                                             'you must supply a positive value')
313
        revno = len(revs) - offset + 1
314
        try:
315
            revision_id = branch.get_rev_id(revno, revs)
316
        except errors.NoSuchRevision:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
317
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
318
        return RevisionInfo(branch, revno, revision_id)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
319
320
SPEC_TYPES.append(RevisionSpec_last)
321
322
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
323
class RevisionSpec_before(RevisionSpec):
324
325
    prefix = 'before:'
326
    
327
    def _match_on(self, branch, revs):
1948.4.33 by John Arbash Meinel
Switch from get_revision_spec() to RevisionSpec.from_string() (as advised by Martin)
328
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
1948.4.13 by John Arbash Meinel
Going before:0 is an error, and if you are on another history, use the leftmost parent
329
        if r.revno == 0:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
330
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
1948.4.13 by John Arbash Meinel
Going before:0 is an error, and if you are on another history, use the leftmost parent
331
                                         'cannot go before the null: revision')
332
        if r.revno is None:
333
            # We need to use the repository history here
334
            rev = branch.repository.get_revision(r.rev_id)
335
            if not rev.parent_ids:
336
                revno = 0
337
                revision_id = None
338
            else:
339
                revision_id = rev.parent_ids[0]
340
                try:
341
                    revno = revs.index(revision_id) + 1
342
                except ValueError:
343
                    revno = None
344
        else:
345
            revno = r.revno - 1
346
            try:
347
                revision_id = branch.get_rev_id(revno, revs)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
348
            except errors.NoSuchRevision:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
349
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.13 by John Arbash Meinel
Going before:0 is an error, and if you are on another history, use the leftmost parent
350
                                                 branch)
351
        return RevisionInfo(branch, revno, revision_id)
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
352
353
SPEC_TYPES.append(RevisionSpec_before)
354
355
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
356
class RevisionSpec_tag(RevisionSpec):
357
    prefix = 'tag:'
358
359
    def _match_on(self, branch, revs):
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
360
        raise errors.InvalidRevisionSpec(self.user_spec, branch,
1948.4.11 by John Arbash Meinel
Update and test the tag: spec
361
                                         'tag: namespace registered,'
362
                                         ' but not implemented')
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
363
364
SPEC_TYPES.append(RevisionSpec_tag)
365
366
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
367
class _RevListToTimestamps(object):
368
    """This takes a list of revisions, and allows you to bisect by date"""
369
370
    __slots__ = ['revs', 'branch']
371
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
372
    def __init__(self, revs, branch):
373
        self.revs = revs
374
        self.branch = branch
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
375
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
376
    def __getitem__(self, index):
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
377
        """Get the date of the index'd item"""
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
378
        r = self.branch.repository.get_revision(self.revs[index])
379
        # TODO: Handle timezone.
380
        return datetime.datetime.fromtimestamp(r.timestamp)
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
381
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
382
    def __len__(self):
383
        return len(self.revs)
384
385
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
386
class RevisionSpec_date(RevisionSpec):
387
    prefix = 'date:'
388
    _date_re = re.compile(
389
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
390
            r'(,|T)?\s*'
391
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
392
        )
393
394
    def _match_on(self, branch, revs):
395
        """
396
        Spec for date revisions:
397
          date:value
398
          value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
399
          matches the first entry after a given date (either at midnight or
400
          at a specified time).
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
401
402
          So the proper way of saying 'give me all entries for today' is:
1711.2.90 by John Arbash Meinel
Fix the docstring of RevisionSpec_date (bug #31276)
403
              -r date:yesterday..date:today
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
404
        """
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
405
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
406
        if self.spec.lower() == 'yesterday':
407
            dt = today - datetime.timedelta(days=1)
408
        elif self.spec.lower() == 'today':
409
            dt = today
410
        elif self.spec.lower() == 'tomorrow':
411
            dt = today + datetime.timedelta(days=1)
412
        else:
413
            m = self._date_re.match(self.spec)
414
            if not m or (not m.group('date') and not m.group('time')):
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
415
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
416
                                                 branch, 'invalid date')
417
418
            try:
419
                if m.group('date'):
420
                    year = int(m.group('year'))
421
                    month = int(m.group('month'))
422
                    day = int(m.group('day'))
423
                else:
424
                    year = today.year
425
                    month = today.month
426
                    day = today.day
427
428
                if m.group('time'):
429
                    hour = int(m.group('hour'))
430
                    minute = int(m.group('minute'))
431
                    if m.group('second'):
432
                        second = int(m.group('second'))
433
                    else:
434
                        second = 0
435
                else:
436
                    hour, minute, second = 0,0,0
437
            except ValueError:
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
438
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
439
                                                 branch, 'invalid date')
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
440
441
            dt = datetime.datetime(year=year, month=month, day=day,
442
                    hour=hour, minute=minute, second=second)
1704.2.27 by Martin Pool
Run bisection search for revision date with lock held. (Robert Widhopf-Frenk)
443
        branch.lock_read()
444
        try:
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
445
            rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
1704.2.27 by Martin Pool
Run bisection search for revision date with lock held. (Robert Widhopf-Frenk)
446
        finally:
447
            branch.unlock()
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
448
        if rev == len(revs):
449
            return RevisionInfo(branch, None)
450
        else:
451
            return RevisionInfo(branch, rev + 1)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
452
453
SPEC_TYPES.append(RevisionSpec_date)
454
455
456
class RevisionSpec_ancestor(RevisionSpec):
457
    prefix = 'ancestor:'
458
459
    def _match_on(self, branch, revs):
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
460
        from bzrlib.branch import Branch
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
461
1948.4.27 by John Arbash Meinel
Deprecate calling RevisionSpec directly, and instead use a helper function. Also merge the old RevisionSpec_int class into RevisionSpec_revno
462
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
1948.4.17 by John Arbash Meinel
Update tests for ancestor: spec
463
        other_branch = Branch.open(self.spec)
1390 by Robert Collins
pair programming worx... merge integration and weave
464
        revision_a = branch.last_revision()
465
        revision_b = other_branch.last_revision()
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
466
        for r, b in ((revision_a, branch), (revision_b, other_branch)):
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
467
            if r in (None, revision.NULL_REVISION):
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
468
                raise errors.NoCommits(b)
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
469
        revision_source = revision.MultipleRevisionSources(
470
                branch.repository, other_branch.repository)
471
        rev_id = revision.common_ancestor(revision_a, revision_b,
472
                                          revision_source)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
473
        try:
474
            revno = branch.revision_id_to_revno(rev_id)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
475
        except errors.NoSuchRevision:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
476
            revno = None
477
        return RevisionInfo(branch, revno, rev_id)
478
        
479
SPEC_TYPES.append(RevisionSpec_ancestor)
1432 by Robert Collins
branch: namespace
480
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
481
1432 by Robert Collins
branch: namespace
482
class RevisionSpec_branch(RevisionSpec):
483
    """A branch: revision specifier.
484
485
    This takes the path to a branch and returns its tip revision id.
486
    """
487
    prefix = 'branch:'
488
489
    def _match_on(self, branch, revs):
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
490
        from bzrlib.branch import Branch
491
        other_branch = Branch.open(self.spec)
1432 by Robert Collins
branch: namespace
492
        revision_b = other_branch.last_revision()
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
493
        if revision_b in (None, revision.NULL_REVISION):
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
494
            raise errors.NoCommits(other_branch)
1432 by Robert Collins
branch: namespace
495
        # pull in the remote revisions so we can diff
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
496
        branch.fetch(other_branch, revision_b)
1432 by Robert Collins
branch: namespace
497
        try:
498
            revno = branch.revision_id_to_revno(revision_b)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
499
        except errors.NoSuchRevision:
1432 by Robert Collins
branch: namespace
500
            revno = None
501
        return RevisionInfo(branch, revno, revision_b)
502
        
503
SPEC_TYPES.append(RevisionSpec_branch)