/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: Robert Collins
  • Date: 2008-09-25 22:25:09 UTC
  • mto: (3735.2.2 repository)
  • mto: This revision was merged to the branch mainline in revision 3749.
  • Revision ID: robertc@robertcollins.net-20080925222509-1cpsw55esrjycw9e
Remove 1.5 series dev formats and document development2 a little better.

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
16
 
 
17
 
 
18
 
import re
19
 
 
20
 
from bzrlib.lazy_import import lazy_import
21
 
lazy_import(globals(), """
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
22
18
import bisect
23
19
import datetime
24
 
""")
 
20
import re
25
21
 
26
22
from bzrlib import (
27
23
    errors,
28
24
    osutils,
29
 
    registry,
30
25
    revision,
31
26
    symbol_versioning,
32
27
    trace,
 
28
    tsort,
33
29
    )
34
30
 
35
31
 
113
109
        return RevisionInfo(branch, revno, revision_id)
114
110
 
115
111
 
 
112
# classes in this list should have a "prefix" attribute, against which
 
113
# string specs are matched
 
114
SPEC_TYPES = []
116
115
_revno_regex = None
117
116
 
118
117
 
121
120
 
122
121
    help_txt = """A parsed revision specification.
123
122
 
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.
 
123
    A revision specification can be an integer, in which case it is
 
124
    assumed to be a revno (though this will translate negative values
 
125
    into positive ones); or it can be a string, in which case it is
 
126
    parsed for something like 'date:' or 'revid:' etc.
128
127
 
129
128
    Revision specs are an UI element, and they have been moved out
130
129
    of the branch class to leave "back-end" classes unaware of such
137
136
 
138
137
    prefix = None
139
138
    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
 
    """
 
139
 
 
140
    def __new__(cls, spec, _internal=False):
 
141
        if _internal:
 
142
            return object.__new__(cls, spec, _internal=_internal)
 
143
 
 
144
        symbol_versioning.warn('Creating a RevisionSpec directly has'
 
145
                               ' been deprecated in version 0.11. Use'
 
146
                               ' RevisionSpec.from_string()'
 
147
                               ' instead.',
 
148
                               DeprecationWarning, stacklevel=2)
 
149
        return RevisionSpec.from_string(spec)
148
150
 
149
151
    @staticmethod
150
152
    def from_string(spec):
159
161
 
160
162
        if spec is None:
161
163
            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)
 
164
        for spectype in SPEC_TYPES:
 
165
            if spec.startswith(spectype.prefix):
 
166
                trace.mutter('Returning RevisionSpec %s for %s',
 
167
                             spectype.__name__, spec)
 
168
                return spectype(spec, _internal=True)
168
169
        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)
 
170
            # RevisionSpec_revno is special cased, because it is the only
 
171
            # one that directly handles plain integers
 
172
            # TODO: This should not be special cased rather it should be
 
173
            # a method invocation on spectype.canparse()
 
174
            global _revno_regex
 
175
            if _revno_regex is None:
 
176
                _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
 
177
            if _revno_regex.match(spec) is not None:
 
178
                return RevisionSpec_revno(spec, _internal=True)
 
179
 
 
180
            raise errors.NoSuchRevisionSpec(spec)
177
181
 
178
182
    def __init__(self, spec, _internal=False):
179
183
        """Create a RevisionSpec referring to the Null revision.
183
187
            called directly. Only from RevisionSpec.from_string()
184
188
        """
185
189
        if not _internal:
 
190
            # XXX: Update this after 0.10 is released
186
191
            symbol_versioning.warn('Creating a RevisionSpec directly has'
187
192
                                   ' been deprecated in version 0.11. Use'
188
193
                                   ' RevisionSpec.from_string()'
270
275
        # this is mostly for helping with testing
271
276
        return '<%s %s>' % (self.__class__.__name__,
272
277
                              self.user_spec)
273
 
 
 
278
    
274
279
    def needs_branch(self):
275
280
        """Whether this revision spec needs a branch.
276
281
 
280
285
 
281
286
    def get_branch(self):
282
287
        """When the revision specifier contains a branch location, return it.
283
 
 
 
288
        
284
289
        Otherwise, return None.
285
290
        """
286
291
        return None
288
293
 
289
294
# private API
290
295
 
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
296
class RevisionSpec_revno(RevisionSpec):
338
297
    """Selects a revision using a number."""
339
298
 
340
299
    help_txt = """Selects a revision using a number.
341
300
 
342
301
    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.
 
302
    Optionally a branch can be specified. The 'revno:' prefix is optional.
 
303
    A negative number will count from the end of the branch (-1 is the
 
304
    last revision, -2 the previous one). If the negative number is larger
 
305
    than the branch's history, the first revision is returned.
347
306
    Examples::
348
307
 
349
308
      revno:1                   -> return the first revision of this branch
383
342
                dotted = False
384
343
            except ValueError:
385
344
                # dotted decimal. This arguably should not be here
386
 
                # but the from_string method is a little primitive
 
345
                # but the from_string method is a little primitive 
387
346
                # right now - RBC 20060928
388
347
                try:
389
348
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
401
360
            revs_or_none = None
402
361
 
403
362
        if dotted:
 
363
            branch.lock_read()
404
364
            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)
 
365
                revision_id_to_revno = branch.get_revision_id_to_revno_map()
 
366
                revisions = [revision_id for revision_id, revno
 
367
                             in revision_id_to_revno.iteritems()
 
368
                             if revno == match_revno]
 
369
            finally:
 
370
                branch.unlock()
 
371
            if len(revisions) != 1:
 
372
                return branch, None, None
409
373
            else:
410
374
                # there is no traditional 'revno' for dotted-decimal revnos.
411
375
                # so for  API compatability we return None.
412
 
                return branch, None, revision_id
 
376
                return branch, None, revisions[0]
413
377
        else:
414
378
            last_revno, last_revision_id = branch.last_revision_info()
415
379
            if revno < 0:
439
403
        else:
440
404
            return self.spec[self.spec.find(':')+1:]
441
405
 
442
 
# Old compatibility
 
406
# Old compatibility 
443
407
RevisionSpec_int = RevisionSpec_revno
444
408
 
 
409
SPEC_TYPES.append(RevisionSpec_revno)
445
410
 
446
411
 
447
412
class RevisionSpec_revid(RevisionSpec):
450
415
    help_txt = """Selects a revision using the revision id.
451
416
 
452
417
    Supply a specific revision id, that can be used to specify any
453
 
    revision id in the ancestry of the branch.
 
418
    revision id in the ancestry of the branch. 
454
419
    Including merges, and pending merges.
455
420
    Examples::
456
421
 
469
434
    def _as_revision_id(self, context_branch):
470
435
        return osutils.safe_revision_id(self.spec, warn=False)
471
436
 
 
437
SPEC_TYPES.append(RevisionSpec_revid)
472
438
 
473
439
 
474
440
class RevisionSpec_last(RevisionSpec):
520
486
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
521
487
        return revision_id
522
488
 
 
489
SPEC_TYPES.append(RevisionSpec_last)
523
490
 
524
491
 
525
492
class RevisionSpec_before(RevisionSpec):
545
512
    """
546
513
 
547
514
    prefix = 'before:'
548
 
 
 
515
    
549
516
    def _match_on(self, branch, revs):
550
517
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
551
518
        if r.revno == 0:
594
561
                'No parents for revision.')
595
562
        return parents[0]
596
563
 
 
564
SPEC_TYPES.append(RevisionSpec_before)
597
565
 
598
566
 
599
567
class RevisionSpec_tag(RevisionSpec):
605
573
    """
606
574
 
607
575
    prefix = 'tag:'
608
 
    dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
609
576
 
610
577
    def _match_on(self, branch, revs):
611
578
        # Can raise tags not supported, NoSuchTag, etc
616
583
    def _as_revision_id(self, context_branch):
617
584
        return context_branch.tags.lookup_tag(self.spec)
618
585
 
 
586
SPEC_TYPES.append(RevisionSpec_tag)
619
587
 
620
588
 
621
589
class _RevListToTimestamps(object):
656
624
      date:yesterday            -> select the first revision since yesterday
657
625
      date:2006-08-14,17:10:14  -> select the first revision after
658
626
                                   August 14th, 2006 at 5:10pm.
659
 
    """
 
627
    """    
660
628
    prefix = 'date:'
661
629
    _date_re = re.compile(
662
630
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
722
690
        else:
723
691
            return RevisionInfo(branch, rev + 1)
724
692
 
 
693
SPEC_TYPES.append(RevisionSpec_date)
725
694
 
726
695
 
727
696
class RevisionSpec_ancestor(RevisionSpec):
772
741
            revision_a = revision.ensure_null(branch.last_revision())
773
742
            if revision_a == revision.NULL_REVISION:
774
743
                raise errors.NoCommits(branch)
775
 
            if other_location == '':
776
 
                other_location = branch.get_parent()
777
744
            other_branch = Branch.open(other_location)
778
745
            other_branch.lock_read()
779
746
            try:
791
758
            branch.unlock()
792
759
 
793
760
 
 
761
SPEC_TYPES.append(RevisionSpec_ancestor)
794
762
 
795
763
 
796
764
class RevisionSpec_branch(RevisionSpec):
805
773
      branch:/path/to/branch
806
774
    """
807
775
    prefix = 'branch:'
808
 
    dwim_catchable_exceptions = (errors.NotBranchError,)
809
776
 
810
777
    def _match_on(self, branch, revs):
811
778
        from bzrlib.branch import Branch
840
807
            raise errors.NoCommits(other_branch)
841
808
        return other_branch.repository.revision_tree(last_revision)
842
809
 
 
810
SPEC_TYPES.append(RevisionSpec_branch)
843
811
 
844
812
 
845
813
class RevisionSpec_submit(RevisionSpec_ancestor):
884
852
            self._get_submit_location(context_branch))
885
853
 
886
854
 
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", [])
 
855
SPEC_TYPES.append(RevisionSpec_submit)