~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

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
18
 
 
19
17
 
20
18
from bzrlib.lazy_import import lazy_import
21
19
lazy_import(globals(), """
40
38
    )
41
39
 
42
40
 
 
41
_marker = []
 
42
 
 
43
 
43
44
class RevisionInfo(object):
44
45
    """The results of applying a revision specification to a branch."""
45
46
 
57
58
    or treat the result as a tuple.
58
59
    """
59
60
 
60
 
    def __init__(self, branch, revno=None, rev_id=None):
 
61
    def __init__(self, branch, revno, rev_id=_marker):
61
62
        self.branch = branch
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:
 
63
        self.revno = revno
 
64
        if rev_id is _marker:
66
65
            # allow caller to be lazy
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
 
66
            if self.revno is None:
 
67
                self.rev_id = None
 
68
            else:
 
69
                self.rev_id = branch.get_rev_id(self.revno)
 
70
        else:
 
71
            self.rev_id = rev_id
78
72
 
79
73
    def __nonzero__(self):
 
74
        # first the easy ones...
80
75
        if self.rev_id is None:
81
76
            return False
 
77
        if self.revno is not None:
 
78
            return True
82
79
        # TODO: otherwise, it should depend on how I was built -
83
80
        # if it's in_history(branch), then check revision_history(),
84
81
        # if it's in_store(branch), do the check below
107
104
            self.revno, self.rev_id, self.branch)
108
105
 
109
106
    @staticmethod
110
 
    def from_revision_id(branch, revision_id, revs=symbol_versioning.DEPRECATED_PARAMETER):
 
107
    def from_revision_id(branch, revision_id, revs):
111
108
        """Construct a RevisionInfo given just the id.
112
109
 
113
110
        Use this if you don't know or care what the revno is.
114
111
        """
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)
 
112
        if revision_id == revision.NULL_REVISION:
 
113
            return RevisionInfo(branch, 0, revision_id)
 
114
        try:
 
115
            revno = revs.index(revision_id) + 1
 
116
        except ValueError:
 
117
            revno = None
 
118
        return RevisionInfo(branch, revno, revision_id)
121
119
 
122
120
 
123
121
class RevisionSpec(object):
140
138
    """
141
139
 
142
140
    prefix = None
143
 
    # wants_revision_history has been deprecated in 2.5.
144
 
    wants_revision_history = False
 
141
    wants_revision_history = True
145
142
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
146
143
    """Exceptions that RevisionSpec_dwim._match_on will catch.
147
144
 
212
209
    def in_history(self, branch):
213
210
        if branch:
214
211
            if self.wants_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()
 
212
                revs = branch.revision_history()
227
213
            else:
228
214
                revs = None
229
215
        else:
309
295
    """
310
296
 
311
297
    help_txt = None
 
298
    # We don't need to build the revision history ourself, that's delegated to
 
299
    # each revspec we try.
 
300
    wants_revision_history = False
312
301
 
313
302
    _revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
314
303
 
392
381
                                   your history is very long.
393
382
    """
394
383
    prefix = 'revno:'
 
384
    wants_revision_history = False
395
385
 
396
386
    def _match_on(self, branch, revs):
397
387
        """Lookup a revision by revision number"""
398
 
        branch, revno, revision_id = self._lookup(branch)
 
388
        branch, revno, revision_id = self._lookup(branch, revs)
399
389
        return RevisionInfo(branch, revno, revision_id)
400
390
 
401
 
    def _lookup(self, branch):
 
391
    def _lookup(self, branch, revs_or_none):
402
392
        loc = self.spec.find(':')
403
393
        if loc == -1:
404
394
            revno_spec = self.spec
428
418
                dotted = True
429
419
 
430
420
        if branch_spec:
431
 
            # the user has overriden the branch to look in.
432
 
            branch = _mod_branch.Branch.open(branch_spec)
 
421
            # the user has override the branch to look in.
 
422
            # we need to refresh the revision_history map and
 
423
            # the branch object.
 
424
            from bzrlib.branch import Branch
 
425
            branch = Branch.open(branch_spec)
 
426
            revs_or_none = None
433
427
 
434
428
        if dotted:
435
429
            try:
439
433
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
440
434
            else:
441
435
                # there is no traditional 'revno' for dotted-decimal revnos.
442
 
                # so for API compatibility we return None.
 
436
                # so for  API compatability we return None.
443
437
                return branch, None, revision_id
444
438
        else:
445
439
            last_revno, last_revision_id = branch.last_revision_info()
451
445
                else:
452
446
                    revno = last_revno + revno + 1
453
447
            try:
454
 
                revision_id = branch.get_rev_id(revno)
 
448
                revision_id = branch.get_rev_id(revno, revs_or_none)
455
449
            except errors.NoSuchRevision:
456
450
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
457
451
        return branch, revno, revision_id
458
452
 
459
453
    def _as_revision_id(self, context_branch):
460
454
        # We would have the revno here, but we don't really care
461
 
        branch, revno, revision_id = self._lookup(context_branch)
 
455
        branch, revno, revision_id = self._lookup(context_branch, None)
462
456
        return revision_id
463
457
 
464
458
    def needs_branch(self):
474
468
RevisionSpec_int = RevisionSpec_revno
475
469
 
476
470
 
 
471
 
477
472
class RevisionIDSpec(RevisionSpec):
478
473
 
479
474
    def _match_on(self, branch, revs):
480
475
        revision_id = self.as_revision_id(branch)
481
 
        return RevisionInfo.from_revision_id(branch, revision_id)
 
476
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
482
477
 
483
478
 
484
479
class RevisionSpec_revid(RevisionIDSpec):
520
515
    prefix = 'last:'
521
516
 
522
517
    def _match_on(self, branch, revs):
523
 
        revno, revision_id = self._revno_and_revision_id(branch)
 
518
        revno, revision_id = self._revno_and_revision_id(branch, revs)
524
519
        return RevisionInfo(branch, revno, revision_id)
525
520
 
526
 
    def _revno_and_revision_id(self, context_branch):
 
521
    def _revno_and_revision_id(self, context_branch, revs_or_none):
527
522
        last_revno, last_revision_id = context_branch.last_revision_info()
528
523
 
529
524
        if self.spec == '':
542
537
 
543
538
        revno = last_revno - offset + 1
544
539
        try:
545
 
            revision_id = context_branch.get_rev_id(revno)
 
540
            revision_id = context_branch.get_rev_id(revno, revs_or_none)
546
541
        except errors.NoSuchRevision:
547
542
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
548
543
        return revno, revision_id
550
545
    def _as_revision_id(self, context_branch):
551
546
        # We compute the revno as part of the process, but we don't really care
552
547
        # about it.
553
 
        revno, revision_id = self._revno_and_revision_id(context_branch)
 
548
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
554
549
        return revision_id
555
550
 
556
551
 
588
583
            # We need to use the repository history here
589
584
            rev = branch.repository.get_revision(r.rev_id)
590
585
            if not rev.parent_ids:
 
586
                revno = 0
591
587
                revision_id = revision.NULL_REVISION
592
588
            else:
593
589
                revision_id = rev.parent_ids[0]
594
 
            revno = None
 
590
                try:
 
591
                    revno = revs.index(revision_id) + 1
 
592
                except ValueError:
 
593
                    revno = None
595
594
        else:
596
595
            revno = r.revno - 1
597
596
            try:
602
601
        return RevisionInfo(branch, revno, revision_id)
603
602
 
604
603
    def _as_revision_id(self, context_branch):
605
 
        base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
 
604
        base_revspec = RevisionSpec.from_string(self.spec)
 
605
        base_revision_id = base_revspec.as_revision_id(context_branch)
606
606
        if base_revision_id == revision.NULL_REVISION:
607
607
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
608
608
                                         'cannot go before the null: revision')
638
638
    def _match_on(self, branch, revs):
639
639
        # Can raise tags not supported, NoSuchTag, etc
640
640
        return RevisionInfo.from_revision_id(branch,
641
 
            branch.tags.lookup_tag(self.spec))
 
641
            branch.tags.lookup_tag(self.spec),
 
642
            revs)
642
643
 
643
644
    def _as_revision_id(self, context_branch):
644
645
        return context_branch.tags.lookup_tag(self.spec)
648
649
class _RevListToTimestamps(object):
649
650
    """This takes a list of revisions, and allows you to bisect by date"""
650
651
 
651
 
    __slots__ = ['branch']
 
652
    __slots__ = ['revs', 'branch']
652
653
 
653
 
    def __init__(self, branch):
 
654
    def __init__(self, revs, branch):
 
655
        self.revs = revs
654
656
        self.branch = branch
655
657
 
656
658
    def __getitem__(self, index):
657
659
        """Get the date of the index'd item"""
658
 
        r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
 
660
        r = self.branch.repository.get_revision(self.revs[index])
659
661
        # TODO: Handle timezone.
660
662
        return datetime.datetime.fromtimestamp(r.timestamp)
661
663
 
662
664
    def __len__(self):
663
 
        return self.branch.revno()
 
665
        return len(self.revs)
664
666
 
665
667
 
666
668
class RevisionSpec_date(RevisionSpec):
740
742
                    hour=hour, minute=minute, second=second)
741
743
        branch.lock_read()
742
744
        try:
743
 
            rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
 
745
            rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
744
746
        finally:
745
747
            branch.unlock()
746
 
        if rev == branch.revno():
 
748
        if rev == len(revs):
747
749
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
748
 
        return RevisionInfo(branch, rev)
 
750
        else:
 
751
            return RevisionInfo(branch, rev + 1)
749
752
 
750
753
 
751
754
 
782
785
    def _find_revision_info(branch, other_location):
783
786
        revision_id = RevisionSpec_ancestor._find_revision_id(branch,
784
787
                                                              other_location)
785
 
        return RevisionInfo(branch, None, revision_id)
 
788
        try:
 
789
            revno = branch.revision_id_to_revno(revision_id)
 
790
        except errors.NoSuchRevision:
 
791
            revno = None
 
792
        return RevisionInfo(branch, revno, revision_id)
786
793
 
787
794
    @staticmethod
788
795
    def _find_revision_id(branch, other_location):
842
849
                branch.fetch(other_branch, revision_b)
843
850
            except errors.ReadOnlyError:
844
851
                branch = other_branch
845
 
        return RevisionInfo(branch, None, revision_b)
 
852
        try:
 
853
            revno = branch.revision_id_to_revno(revision_b)
 
854
        except errors.NoSuchRevision:
 
855
            revno = None
 
856
        return RevisionInfo(branch, revno, revision_b)
846
857
 
847
858
    def _as_revision_id(self, context_branch):
848
859
        from bzrlib.branch import Branch