~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

(vila) Revise legal option names to be less drastic. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
17
18
 
18
 
import re
19
19
 
20
20
from bzrlib.lazy_import import lazy_import
21
21
lazy_import(globals(), """
23
23
import datetime
24
24
 
25
25
from bzrlib import (
26
 
    workingtree,
27
 
    )
28
 
""")
29
 
 
30
 
from bzrlib import (
31
26
    branch as _mod_branch,
32
 
    errors,
33
27
    osutils,
34
 
    registry,
35
28
    revision,
36
29
    symbol_versioning,
 
30
    workingtree,
 
31
    )
 
32
from bzrlib.i18n import gettext
 
33
""")
 
34
 
 
35
from bzrlib import (
 
36
    errors,
 
37
    lazy_regex,
 
38
    registry,
37
39
    trace,
38
 
    workingtree,
39
40
    )
40
41
 
41
42
 
42
 
_marker = []
43
 
 
44
 
 
45
43
class RevisionInfo(object):
46
44
    """The results of applying a revision specification to a branch."""
47
45
 
59
57
    or treat the result as a tuple.
60
58
    """
61
59
 
62
 
    def __init__(self, branch, revno, rev_id=_marker):
 
60
    def __init__(self, branch, revno=None, rev_id=None):
63
61
        self.branch = branch
64
 
        self.revno = revno
65
 
        if rev_id is _marker:
 
62
        self._has_revno = (revno is not None)
 
63
        self._revno = revno
 
64
        self.rev_id = rev_id
 
65
        if self.rev_id is None and self._revno is not None:
66
66
            # allow caller to be lazy
67
 
            if self.revno is None:
68
 
                self.rev_id = None
69
 
            else:
70
 
                self.rev_id = branch.get_rev_id(self.revno)
71
 
        else:
72
 
            self.rev_id = rev_id
 
67
            self.rev_id = branch.get_rev_id(self._revno)
 
68
 
 
69
    @property
 
70
    def revno(self):
 
71
        if not self._has_revno and self.rev_id is not None:
 
72
            try:
 
73
                self._revno = self.branch.revision_id_to_revno(self.rev_id)
 
74
            except errors.NoSuchRevision:
 
75
                self._revno = None
 
76
            self._has_revno = True
 
77
        return self._revno
73
78
 
74
79
    def __nonzero__(self):
75
 
        # first the easy ones...
76
80
        if self.rev_id is None:
77
81
            return False
78
 
        if self.revno is not None:
79
 
            return True
80
82
        # TODO: otherwise, it should depend on how I was built -
81
83
        # if it's in_history(branch), then check revision_history(),
82
84
        # if it's in_store(branch), do the check below
105
107
            self.revno, self.rev_id, self.branch)
106
108
 
107
109
    @staticmethod
108
 
    def from_revision_id(branch, revision_id, revs):
 
110
    def from_revision_id(branch, revision_id, revs=symbol_versioning.DEPRECATED_PARAMETER):
109
111
        """Construct a RevisionInfo given just the id.
110
112
 
111
113
        Use this if you don't know or care what the revno is.
112
114
        """
113
 
        if revision_id == revision.NULL_REVISION:
114
 
            return RevisionInfo(branch, 0, revision_id)
115
 
        try:
116
 
            revno = revs.index(revision_id) + 1
117
 
        except ValueError:
118
 
            revno = None
119
 
        return RevisionInfo(branch, revno, revision_id)
120
 
 
121
 
 
122
 
_revno_regex = None
 
115
        if symbol_versioning.deprecated_passed(revs):
 
116
            symbol_versioning.warn(
 
117
                'RevisionInfo.from_revision_id(revs) was deprecated in 2.5.',
 
118
                DeprecationWarning,
 
119
                stacklevel=2)
 
120
        return RevisionInfo(branch, revno=None, rev_id=revision_id)
123
121
 
124
122
 
125
123
class RevisionSpec(object):
142
140
    """
143
141
 
144
142
    prefix = None
145
 
    wants_revision_history = True
 
143
    # wants_revision_history has been deprecated in 2.5.
 
144
    wants_revision_history = False
146
145
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
147
146
    """Exceptions that RevisionSpec_dwim._match_on will catch.
148
147
 
213
212
    def in_history(self, branch):
214
213
        if branch:
215
214
            if self.wants_revision_history:
216
 
                revs = branch.revision_history()
 
215
                symbol_versioning.warn(
 
216
                    "RevisionSpec.wants_revision_history was "
 
217
                    "deprecated in 2.5 (%s)." % self.__class__.__name__,
 
218
                    DeprecationWarning)
 
219
                branch.lock_read()
 
220
                try:
 
221
                    graph = branch.repository.get_graph()
 
222
                    revs = list(graph.iter_lefthand_ancestry(
 
223
                        branch.last_revision(), [revision.NULL_REVISION]))
 
224
                finally:
 
225
                    branch.unlock()
 
226
                revs.reverse()
217
227
            else:
218
228
                revs = None
219
229
        else:
299
309
    """
300
310
 
301
311
    help_txt = None
302
 
    # We don't need to build the revision history ourself, that's delegated to
303
 
    # each revspec we try.
304
 
    wants_revision_history = False
 
312
 
 
313
    _revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
305
314
 
306
315
    # The revspecs to try
307
316
    _possible_revspecs = []
316
325
        """Run the lookup and see what we can get."""
317
326
 
318
327
        # First, see if it's a revno
319
 
        global _revno_regex
320
 
        if _revno_regex is None:
321
 
            _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
322
 
        if _revno_regex.match(self.spec) is not None:
 
328
        if self._revno_regex.match(self.spec) is not None:
323
329
            try:
324
330
                return self._try_spectype(RevisionSpec_revno, branch)
325
331
            except RevisionSpec_revno.dwim_catchable_exceptions:
386
392
                                   your history is very long.
387
393
    """
388
394
    prefix = 'revno:'
389
 
    wants_revision_history = False
390
395
 
391
396
    def _match_on(self, branch, revs):
392
397
        """Lookup a revision by revision number"""
393
 
        branch, revno, revision_id = self._lookup(branch, revs)
 
398
        branch, revno, revision_id = self._lookup(branch)
394
399
        return RevisionInfo(branch, revno, revision_id)
395
400
 
396
 
    def _lookup(self, branch, revs_or_none):
 
401
    def _lookup(self, branch):
397
402
        loc = self.spec.find(':')
398
403
        if loc == -1:
399
404
            revno_spec = self.spec
423
428
                dotted = True
424
429
 
425
430
        if branch_spec:
426
 
            # the user has override the branch to look in.
427
 
            # we need to refresh the revision_history map and
428
 
            # the branch object.
429
 
            from bzrlib.branch import Branch
430
 
            branch = Branch.open(branch_spec)
431
 
            revs_or_none = None
 
431
            # the user has overriden the branch to look in.
 
432
            branch = _mod_branch.Branch.open(branch_spec)
432
433
 
433
434
        if dotted:
434
435
            try:
438
439
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
439
440
            else:
440
441
                # there is no traditional 'revno' for dotted-decimal revnos.
441
 
                # so for  API compatability we return None.
 
442
                # so for API compatibility we return None.
442
443
                return branch, None, revision_id
443
444
        else:
444
445
            last_revno, last_revision_id = branch.last_revision_info()
450
451
                else:
451
452
                    revno = last_revno + revno + 1
452
453
            try:
453
 
                revision_id = branch.get_rev_id(revno, revs_or_none)
 
454
                revision_id = branch.get_rev_id(revno)
454
455
            except errors.NoSuchRevision:
455
456
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
456
457
        return branch, revno, revision_id
457
458
 
458
459
    def _as_revision_id(self, context_branch):
459
460
        # We would have the revno here, but we don't really care
460
 
        branch, revno, revision_id = self._lookup(context_branch, None)
 
461
        branch, revno, revision_id = self._lookup(context_branch)
461
462
        return revision_id
462
463
 
463
464
    def needs_branch(self):
473
474
RevisionSpec_int = RevisionSpec_revno
474
475
 
475
476
 
476
 
 
477
477
class RevisionIDSpec(RevisionSpec):
478
478
 
479
479
    def _match_on(self, branch, revs):
480
480
        revision_id = self.as_revision_id(branch)
481
 
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
 
481
        return RevisionInfo.from_revision_id(branch, revision_id)
482
482
 
483
483
 
484
484
class RevisionSpec_revid(RevisionIDSpec):
520
520
    prefix = 'last:'
521
521
 
522
522
    def _match_on(self, branch, revs):
523
 
        revno, revision_id = self._revno_and_revision_id(branch, revs)
 
523
        revno, revision_id = self._revno_and_revision_id(branch)
524
524
        return RevisionInfo(branch, revno, revision_id)
525
525
 
526
 
    def _revno_and_revision_id(self, context_branch, revs_or_none):
 
526
    def _revno_and_revision_id(self, context_branch):
527
527
        last_revno, last_revision_id = context_branch.last_revision_info()
528
528
 
529
529
        if self.spec == '':
542
542
 
543
543
        revno = last_revno - offset + 1
544
544
        try:
545
 
            revision_id = context_branch.get_rev_id(revno, revs_or_none)
 
545
            revision_id = context_branch.get_rev_id(revno)
546
546
        except errors.NoSuchRevision:
547
547
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
548
548
        return revno, revision_id
550
550
    def _as_revision_id(self, context_branch):
551
551
        # We compute the revno as part of the process, but we don't really care
552
552
        # about it.
553
 
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
 
553
        revno, revision_id = self._revno_and_revision_id(context_branch)
554
554
        return revision_id
555
555
 
556
556
 
588
588
            # We need to use the repository history here
589
589
            rev = branch.repository.get_revision(r.rev_id)
590
590
            if not rev.parent_ids:
591
 
                revno = 0
592
591
                revision_id = revision.NULL_REVISION
593
592
            else:
594
593
                revision_id = rev.parent_ids[0]
595
 
                try:
596
 
                    revno = revs.index(revision_id) + 1
597
 
                except ValueError:
598
 
                    revno = None
 
594
            revno = None
599
595
        else:
600
596
            revno = r.revno - 1
601
597
            try:
606
602
        return RevisionInfo(branch, revno, revision_id)
607
603
 
608
604
    def _as_revision_id(self, context_branch):
609
 
        base_revspec = RevisionSpec.from_string(self.spec)
610
 
        base_revision_id = base_revspec.as_revision_id(context_branch)
 
605
        base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
611
606
        if base_revision_id == revision.NULL_REVISION:
612
607
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
613
608
                                         'cannot go before the null: revision')
643
638
    def _match_on(self, branch, revs):
644
639
        # Can raise tags not supported, NoSuchTag, etc
645
640
        return RevisionInfo.from_revision_id(branch,
646
 
            branch.tags.lookup_tag(self.spec),
647
 
            revs)
 
641
            branch.tags.lookup_tag(self.spec))
648
642
 
649
643
    def _as_revision_id(self, context_branch):
650
644
        return context_branch.tags.lookup_tag(self.spec)
654
648
class _RevListToTimestamps(object):
655
649
    """This takes a list of revisions, and allows you to bisect by date"""
656
650
 
657
 
    __slots__ = ['revs', 'branch']
 
651
    __slots__ = ['branch']
658
652
 
659
 
    def __init__(self, revs, branch):
660
 
        self.revs = revs
 
653
    def __init__(self, branch):
661
654
        self.branch = branch
662
655
 
663
656
    def __getitem__(self, index):
664
657
        """Get the date of the index'd item"""
665
 
        r = self.branch.repository.get_revision(self.revs[index])
 
658
        r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
666
659
        # TODO: Handle timezone.
667
660
        return datetime.datetime.fromtimestamp(r.timestamp)
668
661
 
669
662
    def __len__(self):
670
 
        return len(self.revs)
 
663
        return self.branch.revno()
671
664
 
672
665
 
673
666
class RevisionSpec_date(RevisionSpec):
691
684
                                   August 14th, 2006 at 5:10pm.
692
685
    """
693
686
    prefix = 'date:'
694
 
    _date_re = re.compile(
 
687
    _date_regex = lazy_regex.lazy_compile(
695
688
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
696
689
            r'(,|T)?\s*'
697
690
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
715
708
        elif self.spec.lower() == 'tomorrow':
716
709
            dt = today + datetime.timedelta(days=1)
717
710
        else:
718
 
            m = self._date_re.match(self.spec)
 
711
            m = self._date_regex.match(self.spec)
719
712
            if not m or (not m.group('date') and not m.group('time')):
720
713
                raise errors.InvalidRevisionSpec(self.user_spec,
721
714
                                                 branch, 'invalid date')
747
740
                    hour=hour, minute=minute, second=second)
748
741
        branch.lock_read()
749
742
        try:
750
 
            rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
 
743
            rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
751
744
        finally:
752
745
            branch.unlock()
753
 
        if rev == len(revs):
 
746
        if rev == branch.revno():
754
747
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
755
 
        else:
756
 
            return RevisionInfo(branch, rev + 1)
 
748
        return RevisionInfo(branch, rev)
757
749
 
758
750
 
759
751
 
790
782
    def _find_revision_info(branch, other_location):
791
783
        revision_id = RevisionSpec_ancestor._find_revision_id(branch,
792
784
                                                              other_location)
793
 
        try:
794
 
            revno = branch.revision_id_to_revno(revision_id)
795
 
        except errors.NoSuchRevision:
796
 
            revno = None
797
 
        return RevisionInfo(branch, revno, revision_id)
 
785
        return RevisionInfo(branch, None, revision_id)
798
786
 
799
787
    @staticmethod
800
788
    def _find_revision_id(branch, other_location):
854
842
                branch.fetch(other_branch, revision_b)
855
843
            except errors.ReadOnlyError:
856
844
                branch = other_branch
857
 
        try:
858
 
            revno = branch.revision_id_to_revno(revision_b)
859
 
        except errors.NoSuchRevision:
860
 
            revno = None
861
 
        return RevisionInfo(branch, revno, revision_b)
 
845
        return RevisionInfo(branch, None, revision_b)
862
846
 
863
847
    def _as_revision_id(self, context_branch):
864
848
        from bzrlib.branch import Branch
916
900
            location_type = 'parent branch'
917
901
        if submit_location is None:
918
902
            raise errors.NoSubmitBranch(branch)
919
 
        trace.note('Using %s %s', location_type, submit_location)
 
903
        trace.note(gettext('Using {0} {1}').format(location_type,
 
904
                                                        submit_location))
920
905
        return submit_location
921
906
 
922
907
    def _match_on(self, branch, revs):