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