/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: Andrew Bennetts
  • Date: 2008-09-14 10:23:29 UTC
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080914102329-2nvv40h7fljpvcxt
Tidy imports in bzrlib/msgeditor.py as suggested by John's review.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
import re
26
26
from bzrlib import (
27
27
    errors,
28
28
    osutils,
29
 
    registry,
30
29
    revision,
31
30
    symbol_versioning,
32
31
    trace,
113
112
        return RevisionInfo(branch, revno, revision_id)
114
113
 
115
114
 
 
115
# classes in this list should have a "prefix" attribute, against which
 
116
# string specs are matched
 
117
SPEC_TYPES = []
116
118
_revno_regex = None
117
119
 
118
120
 
121
123
 
122
124
    help_txt = """A parsed revision specification.
123
125
 
124
 
    A revision specification is a string, which may be unambiguous about
125
 
    what it represents by giving a prefix like 'date:' or 'revid:' etc,
126
 
    or it may have no prefix, in which case it's tried against several
127
 
    specifier types in sequence to determine what the user meant.
 
126
    A revision specification can be an integer, in which case it is
 
127
    assumed to be a revno (though this will translate negative values
 
128
    into positive ones); or it can be a string, in which case it is
 
129
    parsed for something like 'date:' or 'revid:' etc.
128
130
 
129
131
    Revision specs are an UI element, and they have been moved out
130
132
    of the branch class to leave "back-end" classes unaware of such
137
139
 
138
140
    prefix = None
139
141
    wants_revision_history = True
140
 
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
141
 
    """Exceptions that RevisionSpec_dwim._match_on will catch.
142
 
 
143
 
    If the revspec is part of ``dwim_revspecs``, it may be tried with an
144
 
    invalid revspec and raises some exception. The exceptions mentioned here
145
 
    will not be reported to the user but simply ignored without stopping the
146
 
    dwim processing.
147
 
    """
 
142
 
 
143
    def __new__(cls, spec, _internal=False):
 
144
        if _internal:
 
145
            return object.__new__(cls, spec, _internal=_internal)
 
146
 
 
147
        symbol_versioning.warn('Creating a RevisionSpec directly has'
 
148
                               ' been deprecated in version 0.11. Use'
 
149
                               ' RevisionSpec.from_string()'
 
150
                               ' instead.',
 
151
                               DeprecationWarning, stacklevel=2)
 
152
        return RevisionSpec.from_string(spec)
148
153
 
149
154
    @staticmethod
150
155
    def from_string(spec):
159
164
 
160
165
        if spec is None:
161
166
            return RevisionSpec(None, _internal=True)
162
 
        match = revspec_registry.get_prefix(spec)
163
 
        if match is not None:
164
 
            spectype, specsuffix = match
165
 
            trace.mutter('Returning RevisionSpec %s for %s',
166
 
                         spectype.__name__, spec)
167
 
            return spectype(spec, _internal=True)
 
167
        for spectype in SPEC_TYPES:
 
168
            if spec.startswith(spectype.prefix):
 
169
                trace.mutter('Returning RevisionSpec %s for %s',
 
170
                             spectype.__name__, spec)
 
171
                return spectype(spec, _internal=True)
168
172
        else:
169
 
            for spectype in SPEC_TYPES:
170
 
                if spec.startswith(spectype.prefix):
171
 
                    trace.mutter('Returning RevisionSpec %s for %s',
172
 
                                 spectype.__name__, spec)
173
 
                    return spectype(spec, _internal=True)
174
 
            # Otherwise treat it as a DWIM, build the RevisionSpec object and
175
 
            # wait for _match_on to be called.
176
 
            return RevisionSpec_dwim(spec, _internal=True)
 
173
            # RevisionSpec_revno is special cased, because it is the only
 
174
            # one that directly handles plain integers
 
175
            # TODO: This should not be special cased rather it should be
 
176
            # a method invocation on spectype.canparse()
 
177
            global _revno_regex
 
178
            if _revno_regex is None:
 
179
                _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
 
180
            if _revno_regex.match(spec) is not None:
 
181
                return RevisionSpec_revno(spec, _internal=True)
 
182
 
 
183
            raise errors.NoSuchRevisionSpec(spec)
177
184
 
178
185
    def __init__(self, spec, _internal=False):
179
186
        """Create a RevisionSpec referring to the Null revision.
183
190
            called directly. Only from RevisionSpec.from_string()
184
191
        """
185
192
        if not _internal:
 
193
            # XXX: Update this after 0.10 is released
186
194
            symbol_versioning.warn('Creating a RevisionSpec directly has'
187
195
                                   ' been deprecated in version 0.11. Use'
188
196
                                   ' RevisionSpec.from_string()'
247
255
        """
248
256
        return self.in_history(context_branch).rev_id
249
257
 
250
 
    def as_tree(self, context_branch):
251
 
        """Return the tree object for this revisions spec.
252
 
 
253
 
        Some revision specs require a context_branch to be able to determine
254
 
        the revision id and access the repository. Not all specs will make
255
 
        use of it.
256
 
        """
257
 
        return self._as_tree(context_branch)
258
 
 
259
 
    def _as_tree(self, context_branch):
260
 
        """Implementation of as_tree().
261
 
 
262
 
        Classes should override this function to provide appropriate
263
 
        functionality. The default is to just call '.as_revision_id()'
264
 
        and get the revision tree from context_branch's repository.
265
 
        """
266
 
        revision_id = self.as_revision_id(context_branch)
267
 
        return context_branch.repository.revision_tree(revision_id)
268
 
 
269
258
    def __repr__(self):
270
259
        # this is mostly for helping with testing
271
260
        return '<%s %s>' % (self.__class__.__name__,
272
261
                              self.user_spec)
273
 
 
 
262
    
274
263
    def needs_branch(self):
275
264
        """Whether this revision spec needs a branch.
276
265
 
280
269
 
281
270
    def get_branch(self):
282
271
        """When the revision specifier contains a branch location, return it.
283
 
 
 
272
        
284
273
        Otherwise, return None.
285
274
        """
286
275
        return None
288
277
 
289
278
# private API
290
279
 
291
 
class RevisionSpec_dwim(RevisionSpec):
292
 
    """Provides a DWIMish revision specifier lookup.
293
 
 
294
 
    Note that this does not go in the revspec_registry because by definition
295
 
    there is no prefix to identify it.  It's solely called from
296
 
    RevisionSpec.from_string() because the DWIMification happen when _match_on
297
 
    is called so the string describing the revision is kept here until needed.
298
 
    """
299
 
 
300
 
    help_txt = None
301
 
    # We don't need to build the revision history ourself, that's delegated to
302
 
    # each revspec we try.
303
 
    wants_revision_history = False
304
 
 
305
 
    def _try_spectype(self, rstype, branch):
306
 
        rs = rstype(self.spec, _internal=True)
307
 
        # Hit in_history to find out if it exists, or we need to try the
308
 
        # next type.
309
 
        return rs.in_history(branch)
310
 
 
311
 
    def _match_on(self, branch, revs):
312
 
        """Run the lookup and see what we can get."""
313
 
 
314
 
        # First, see if it's a revno
315
 
        global _revno_regex
316
 
        if _revno_regex is None:
317
 
            _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
318
 
        if _revno_regex.match(self.spec) is not None:
319
 
            try:
320
 
                return self._try_spectype(RevisionSpec_revno, branch)
321
 
            except RevisionSpec_revno.dwim_catchable_exceptions:
322
 
                pass
323
 
 
324
 
        # Next see what has been registered
325
 
        for rs_class in dwim_revspecs:
326
 
            try:
327
 
                return self._try_spectype(rs_class, branch)
328
 
            except rs_class.dwim_catchable_exceptions:
329
 
                pass
330
 
 
331
 
        # Well, I dunno what it is. Note that we don't try to keep track of the
332
 
        # first of last exception raised during the DWIM tries as none seems
333
 
        # really relevant.
334
 
        raise errors.InvalidRevisionSpec(self.spec, branch)
335
 
 
336
 
 
337
280
class RevisionSpec_revno(RevisionSpec):
338
281
    """Selects a revision using a number."""
339
282
 
340
283
    help_txt = """Selects a revision using a number.
341
284
 
342
285
    Use an integer to specify a revision in the history of the branch.
343
 
    Optionally a branch can be specified.  A negative number will count
344
 
    from the end of the branch (-1 is the last revision, -2 the previous
345
 
    one). If the negative number is larger than the branch's history, the
346
 
    first revision is returned.
 
286
    Optionally a branch can be specified. The 'revno:' prefix is optional.
 
287
    A negative number will count from the end of the branch (-1 is the
 
288
    last revision, -2 the previous one). If the negative number is larger
 
289
    than the branch's history, the first revision is returned.
347
290
    Examples::
348
291
 
349
 
      revno:1                   -> return the first revision of this branch
 
292
      revno:1                   -> return the first revision
350
293
      revno:3:/path/to/branch   -> return the 3rd revision of
351
294
                                   the branch '/path/to/branch'
352
295
      revno:-1                  -> The last revision in a branch.
383
326
                dotted = False
384
327
            except ValueError:
385
328
                # dotted decimal. This arguably should not be here
386
 
                # but the from_string method is a little primitive
 
329
                # but the from_string method is a little primitive 
387
330
                # right now - RBC 20060928
388
331
                try:
389
332
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
401
344
            revs_or_none = None
402
345
 
403
346
        if dotted:
 
347
            branch.lock_read()
404
348
            try:
405
 
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
406
 
                    _cache_reverse=True)
407
 
            except errors.NoSuchRevision:
408
 
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
349
                revision_id_to_revno = branch.get_revision_id_to_revno_map()
 
350
                revisions = [revision_id for revision_id, revno
 
351
                             in revision_id_to_revno.iteritems()
 
352
                             if revno == match_revno]
 
353
            finally:
 
354
                branch.unlock()
 
355
            if len(revisions) != 1:
 
356
                return branch, None, None
409
357
            else:
410
358
                # there is no traditional 'revno' for dotted-decimal revnos.
411
359
                # so for  API compatability we return None.
412
 
                return branch, None, revision_id
 
360
                return branch, None, revisions[0]
413
361
        else:
414
362
            last_revno, last_revision_id = branch.last_revision_info()
415
363
            if revno < 0:
439
387
        else:
440
388
            return self.spec[self.spec.find(':')+1:]
441
389
 
442
 
# Old compatibility
 
390
# Old compatibility 
443
391
RevisionSpec_int = RevisionSpec_revno
444
392
 
 
393
SPEC_TYPES.append(RevisionSpec_revno)
445
394
 
446
395
 
447
396
class RevisionSpec_revid(RevisionSpec):
450
399
    help_txt = """Selects a revision using the revision id.
451
400
 
452
401
    Supply a specific revision id, that can be used to specify any
453
 
    revision id in the ancestry of the branch.
 
402
    revision id in the ancestry of the branch. 
454
403
    Including merges, and pending merges.
455
404
    Examples::
456
405
 
469
418
    def _as_revision_id(self, context_branch):
470
419
        return osutils.safe_revision_id(self.spec, warn=False)
471
420
 
 
421
SPEC_TYPES.append(RevisionSpec_revid)
472
422
 
473
423
 
474
424
class RevisionSpec_last(RevisionSpec):
520
470
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
521
471
        return revision_id
522
472
 
 
473
SPEC_TYPES.append(RevisionSpec_last)
523
474
 
524
475
 
525
476
class RevisionSpec_before(RevisionSpec):
527
478
 
528
479
    help_txt = """Selects the parent of the revision specified.
529
480
 
530
 
    Supply any revision spec to return the parent of that revision.  This is
531
 
    mostly useful when inspecting revisions that are not in the revision history
532
 
    of a branch.
533
 
 
 
481
    Supply any revision spec to return the parent of that revision.
534
482
    It is an error to request the parent of the null revision (before:0).
 
483
    This is mostly useful when inspecting revisions that are not in the
 
484
    revision history of a branch.
535
485
 
536
486
    Examples::
537
487
 
538
488
      before:1913    -> Return the parent of revno 1913 (revno 1912)
539
489
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
540
490
                                            aaaa@bbbb-1234567890
541
 
      bzr diff -r before:1913..1913
542
 
            -> Find the changes between revision 1913 and its parent (1912).
543
 
               (What changes did revision 1913 introduce).
544
 
               This is equivalent to:  bzr diff -c 1913
 
491
      bzr diff -r before:revid:aaaa..revid:aaaa
 
492
            -> Find the changes between revision 'aaaa' and its parent.
 
493
               (what changes did 'aaaa' introduce)
545
494
    """
546
495
 
547
496
    prefix = 'before:'
548
 
 
 
497
    
549
498
    def _match_on(self, branch, revs):
550
499
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
551
500
        if r.revno == 0:
594
543
                'No parents for revision.')
595
544
        return parents[0]
596
545
 
 
546
SPEC_TYPES.append(RevisionSpec_before)
597
547
 
598
548
 
599
549
class RevisionSpec_tag(RevisionSpec):
605
555
    """
606
556
 
607
557
    prefix = 'tag:'
608
 
    dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
609
558
 
610
559
    def _match_on(self, branch, revs):
611
560
        # Can raise tags not supported, NoSuchTag, etc
616
565
    def _as_revision_id(self, context_branch):
617
566
        return context_branch.tags.lookup_tag(self.spec)
618
567
 
 
568
SPEC_TYPES.append(RevisionSpec_tag)
619
569
 
620
570
 
621
571
class _RevListToTimestamps(object):
656
606
      date:yesterday            -> select the first revision since yesterday
657
607
      date:2006-08-14,17:10:14  -> select the first revision after
658
608
                                   August 14th, 2006 at 5:10pm.
659
 
    """
 
609
    """    
660
610
    prefix = 'date:'
661
611
    _date_re = re.compile(
662
612
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
722
672
        else:
723
673
            return RevisionInfo(branch, rev + 1)
724
674
 
 
675
SPEC_TYPES.append(RevisionSpec_date)
725
676
 
726
677
 
727
678
class RevisionSpec_ancestor(RevisionSpec):
772
723
            revision_a = revision.ensure_null(branch.last_revision())
773
724
            if revision_a == revision.NULL_REVISION:
774
725
                raise errors.NoCommits(branch)
775
 
            if other_location == '':
776
 
                other_location = branch.get_parent()
777
726
            other_branch = Branch.open(other_location)
778
727
            other_branch.lock_read()
779
728
            try:
791
740
            branch.unlock()
792
741
 
793
742
 
 
743
SPEC_TYPES.append(RevisionSpec_ancestor)
794
744
 
795
745
 
796
746
class RevisionSpec_branch(RevisionSpec):
805
755
      branch:/path/to/branch
806
756
    """
807
757
    prefix = 'branch:'
808
 
    dwim_catchable_exceptions = (errors.NotBranchError,)
809
758
 
810
759
    def _match_on(self, branch, revs):
811
760
        from bzrlib.branch import Branch
831
780
            raise errors.NoCommits(other_branch)
832
781
        return last_revision
833
782
 
834
 
    def _as_tree(self, context_branch):
835
 
        from bzrlib.branch import Branch
836
 
        other_branch = Branch.open(self.spec)
837
 
        last_revision = other_branch.last_revision()
838
 
        last_revision = revision.ensure_null(last_revision)
839
 
        if last_revision == revision.NULL_REVISION:
840
 
            raise errors.NoCommits(other_branch)
841
 
        return other_branch.repository.revision_tree(last_revision)
842
 
 
 
783
SPEC_TYPES.append(RevisionSpec_branch)
843
784
 
844
785
 
845
786
class RevisionSpec_submit(RevisionSpec_ancestor):
884
825
            self._get_submit_location(context_branch))
885
826
 
886
827
 
887
 
# The order in which we want to DWIM a revision spec without any prefix.
888
 
# revno is always tried first and isn't listed here, this is used by
889
 
# RevisionSpec_dwim._match_on
890
 
dwim_revspecs = [
891
 
    RevisionSpec_tag, # Let's try for a tag
892
 
    RevisionSpec_revid, # Maybe it's a revid?
893
 
    RevisionSpec_date, # Perhaps a date?
894
 
    RevisionSpec_branch, # OK, last try, maybe it's a branch
895
 
    ]
896
 
 
897
 
 
898
 
revspec_registry = registry.Registry()
899
 
def _register_revspec(revspec):
900
 
    revspec_registry.register(revspec.prefix, revspec)
901
 
 
902
 
_register_revspec(RevisionSpec_revno)
903
 
_register_revspec(RevisionSpec_revid)
904
 
_register_revspec(RevisionSpec_last)
905
 
_register_revspec(RevisionSpec_before)
906
 
_register_revspec(RevisionSpec_tag)
907
 
_register_revspec(RevisionSpec_date)
908
 
_register_revspec(RevisionSpec_ancestor)
909
 
_register_revspec(RevisionSpec_branch)
910
 
_register_revspec(RevisionSpec_submit)
911
 
 
912
 
# classes in this list should have a "prefix" attribute, against which
913
 
# string specs are matched
914
 
SPEC_TYPES = symbol_versioning.deprecated_list(
915
 
    symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])
 
828
SPEC_TYPES.append(RevisionSpec_submit)