~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

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