~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
1907.4.1 by Matthieu Moy
Fixed merge to work nicely with -r revno:N:path
227
    def get_branch(self):
228
        """When the revision specifier contains a branch location, return it.
229
        
230
        Otherwise, return None.
231
        """
232
        return None
233
1907.4.9 by Matthieu Moy
missing newline
234
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
235
# private API
236
237
class RevisionSpec_revno(RevisionSpec):
238
    prefix = 'revno:'
239
240
    def _match_on(self, branch, revs):
241
        """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
242
        loc = self.spec.find(':')
243
        if loc == -1:
244
            revno_spec = self.spec
245
            branch_spec = None
246
        else:
247
            revno_spec = self.spec[:loc]
248
            branch_spec = self.spec[loc+1:]
249
250
        if revno_spec == '':
1948.4.6 by John Arbash Meinel
A small bugfix, and more tests for revno:
251
            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
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, '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
254
            revno = None
255
        else:
256
            try:
257
                revno = int(revno_spec)
258
            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
259
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.5 by John Arbash Meinel
Fix tests for negative entries, and add tests for revno:
260
                                                 branch, e)
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
261
1948.4.6 by John Arbash Meinel
A small bugfix, and more tests for revno:
262
        if branch_spec:
1948.4.1 by John Arbash Meinel
Update number parsers to raise InvalidRevisionSpec. Update revno: itself so it supports negative numbers
263
            from bzrlib.branch import Branch
264
            branch = Branch.open(branch_spec)
1948.4.22 by John Arbash Meinel
Refactor common code from integer revno handlers
265
            # Need to use a new revision history
266
            # because we are using a specific branch
267
            revs = branch.revision_history()
268
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
269
        if revno < 0:
270
            if (-revno) >= len(revs):
271
                revno = 1
272
            else:
273
                revno = len(revs) + revno + 1
274
        try:
275
            revision_id = branch.get_rev_id(revno, revs)
276
        except errors.NoSuchRevision:
277
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
278
        return RevisionInfo(branch, revno, revision_id)
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
279
        
1881.1.4 by Matthieu Moy
needs_tree -> needs_branch
280
    def needs_branch(self):
1881.1.1 by Matthieu Moy
Fixed and tested "bzr diff" outside a working tree.
281
        return self.spec.find(':') == -1
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
282
1907.4.1 by Matthieu Moy
Fixed merge to work nicely with -r revno:N:path
283
    def get_branch(self):
284
        if self.spec.find(':') == -1:
285
            return None
286
        else:
287
            return self.spec[self.spec.find(':')+1:]
288
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
289
# Old compatibility 
290
RevisionSpec_int = RevisionSpec_revno
291
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
292
SPEC_TYPES.append(RevisionSpec_revno)
293
294
295
class RevisionSpec_revid(RevisionSpec):
296
    prefix = 'revid:'
297
298
    def _match_on(self, branch, revs):
299
        try:
1948.4.2 by John Arbash Meinel
Update _match_on_and_check to raise the right error
300
            revno = revs.index(self.spec) + 1
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
301
        except ValueError:
1948.4.2 by John Arbash Meinel
Update _match_on_and_check to raise the right error
302
            revno = None
303
        return RevisionInfo(branch, revno, self.spec)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
304
305
SPEC_TYPES.append(RevisionSpec_revid)
306
307
308
class RevisionSpec_last(RevisionSpec):
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
309
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
310
    prefix = 'last:'
311
312
    def _match_on(self, branch, revs):
1948.4.9 by John Arbash Meinel
Cleanup and test last:
313
        if self.spec == '':
314
            if not revs:
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
315
                raise errors.NoCommits(branch)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
316
            return RevisionInfo(branch, len(revs), revs[-1])
317
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
318
        try:
319
            offset = int(self.spec)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
320
        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
321
            raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
322
323
        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
324
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
1948.4.9 by John Arbash Meinel
Cleanup and test last:
325
                                             'you must supply a positive value')
326
        revno = len(revs) - offset + 1
327
        try:
328
            revision_id = branch.get_rev_id(revno, revs)
329
        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
330
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
1948.4.9 by John Arbash Meinel
Cleanup and test last:
331
        return RevisionInfo(branch, revno, revision_id)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
332
333
SPEC_TYPES.append(RevisionSpec_last)
334
335
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
336
class RevisionSpec_before(RevisionSpec):
337
338
    prefix = 'before:'
339
    
340
    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)
341
        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
342
        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
343
            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
344
                                         'cannot go before the null: revision')
345
        if r.revno is None:
346
            # We need to use the repository history here
347
            rev = branch.repository.get_revision(r.rev_id)
348
            if not rev.parent_ids:
349
                revno = 0
350
                revision_id = None
351
            else:
352
                revision_id = rev.parent_ids[0]
353
                try:
354
                    revno = revs.index(revision_id) + 1
355
                except ValueError:
356
                    revno = None
357
        else:
358
            revno = r.revno - 1
359
            try:
360
                revision_id = branch.get_rev_id(revno, revs)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
361
            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
362
                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
363
                                                 branch)
364
        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
365
366
SPEC_TYPES.append(RevisionSpec_before)
367
368
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
369
class RevisionSpec_tag(RevisionSpec):
370
    prefix = 'tag:'
371
372
    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
373
        raise errors.InvalidRevisionSpec(self.user_spec, branch,
1948.4.11 by John Arbash Meinel
Update and test the tag: spec
374
                                         'tag: namespace registered,'
375
                                         ' but not implemented')
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
376
377
SPEC_TYPES.append(RevisionSpec_tag)
378
379
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
380
class _RevListToTimestamps(object):
381
    """This takes a list of revisions, and allows you to bisect by date"""
382
383
    __slots__ = ['revs', 'branch']
384
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
385
    def __init__(self, revs, branch):
386
        self.revs = revs
387
        self.branch = branch
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
388
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
389
    def __getitem__(self, index):
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
390
        """Get the date of the index'd item"""
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
391
        r = self.branch.repository.get_revision(self.revs[index])
392
        # TODO: Handle timezone.
393
        return datetime.datetime.fromtimestamp(r.timestamp)
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
394
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
395
    def __len__(self):
396
        return len(self.revs)
397
398
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
399
class RevisionSpec_date(RevisionSpec):
400
    prefix = 'date:'
401
    _date_re = re.compile(
402
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
403
            r'(,|T)?\s*'
404
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
405
        )
406
407
    def _match_on(self, branch, revs):
408
        """
409
        Spec for date revisions:
410
          date:value
411
          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
412
          matches the first entry after a given date (either at midnight or
413
          at a specified time).
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
414
415
          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)
416
              -r date:yesterday..date:today
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
417
        """
1185.1.39 by Robert Collins
Robey Pointers before: namespace to clear up usage of dates in revision parameters
418
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
419
        if self.spec.lower() == 'yesterday':
420
            dt = today - datetime.timedelta(days=1)
421
        elif self.spec.lower() == 'today':
422
            dt = today
423
        elif self.spec.lower() == 'tomorrow':
424
            dt = today + datetime.timedelta(days=1)
425
        else:
426
            m = self._date_re.match(self.spec)
427
            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
428
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
429
                                                 branch, 'invalid date')
430
431
            try:
432
                if m.group('date'):
433
                    year = int(m.group('year'))
434
                    month = int(m.group('month'))
435
                    day = int(m.group('day'))
436
                else:
437
                    year = today.year
438
                    month = today.month
439
                    day = today.day
440
441
                if m.group('time'):
442
                    hour = int(m.group('hour'))
443
                    minute = int(m.group('minute'))
444
                    if m.group('second'):
445
                        second = int(m.group('second'))
446
                    else:
447
                        second = 0
448
                else:
449
                    hour, minute, second = 0,0,0
450
            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
451
                raise errors.InvalidRevisionSpec(self.user_spec,
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
452
                                                 branch, 'invalid date')
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
453
454
            dt = datetime.datetime(year=year, month=month, day=day,
455
                    hour=hour, minute=minute, second=second)
1704.2.27 by Martin Pool
Run bisection search for revision date with lock held. (Robert Widhopf-Frenk)
456
        branch.lock_read()
457
        try:
1948.4.12 by John Arbash Meinel
Some tests for the date: spec
458
            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)
459
        finally:
460
            branch.unlock()
1688.2.2 by Guillaume Pinot
Binary search for 'date:' revision.
461
        if rev == len(revs):
462
            return RevisionInfo(branch, None)
463
        else:
464
            return RevisionInfo(branch, rev + 1)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
465
466
SPEC_TYPES.append(RevisionSpec_date)
467
468
469
class RevisionSpec_ancestor(RevisionSpec):
470
    prefix = 'ancestor:'
471
472
    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
473
        from bzrlib.branch import Branch
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
474
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
475
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
1948.4.17 by John Arbash Meinel
Update tests for ancestor: spec
476
        other_branch = Branch.open(self.spec)
1390 by Robert Collins
pair programming worx... merge integration and weave
477
        revision_a = branch.last_revision()
478
        revision_b = other_branch.last_revision()
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
479
        for r, b in ((revision_a, branch), (revision_b, other_branch)):
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
480
            if r in (None, revision.NULL_REVISION):
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
481
                raise errors.NoCommits(b)
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
482
        revision_source = revision.MultipleRevisionSources(
483
                branch.repository, other_branch.repository)
484
        rev_id = revision.common_ancestor(revision_a, revision_b,
485
                                          revision_source)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
486
        try:
487
            revno = branch.revision_id_to_revno(rev_id)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
488
        except errors.NoSuchRevision:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
489
            revno = None
490
        return RevisionInfo(branch, revno, rev_id)
491
        
492
SPEC_TYPES.append(RevisionSpec_ancestor)
1432 by Robert Collins
branch: namespace
493
1948.4.16 by John Arbash Meinel
Move the tests into the associated tester, remove redundant tests, some small PEP8 changes
494
1432 by Robert Collins
branch: namespace
495
class RevisionSpec_branch(RevisionSpec):
496
    """A branch: revision specifier.
497
498
    This takes the path to a branch and returns its tip revision id.
499
    """
500
    prefix = 'branch:'
501
502
    def _match_on(self, branch, revs):
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
503
        from bzrlib.branch import Branch
504
        other_branch = Branch.open(self.spec)
1432 by Robert Collins
branch: namespace
505
        revision_b = other_branch.last_revision()
1948.4.18 by John Arbash Meinel
Update branch: spec and tests
506
        if revision_b in (None, revision.NULL_REVISION):
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
507
            raise errors.NoCommits(other_branch)
1432 by Robert Collins
branch: namespace
508
        # 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.
509
        branch.fetch(other_branch, revision_b)
1432 by Robert Collins
branch: namespace
510
        try:
511
            revno = branch.revision_id_to_revno(revision_b)
1948.4.26 by John Arbash Meinel
Get rid of direct imports of exceptions
512
        except errors.NoSuchRevision:
1432 by Robert Collins
branch: namespace
513
            revno = None
514
        return RevisionInfo(branch, revno, revision_b)
515
        
516
SPEC_TYPES.append(RevisionSpec_branch)