/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 brzlib/revisionspec.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

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
 
 
18
 
from .lazy_import import lazy_import
 
17
from __future__ import absolute_import
 
18
 
 
19
 
 
20
from brzlib.lazy_import import lazy_import
19
21
lazy_import(globals(), """
20
22
import bisect
21
23
import datetime
22
24
 
23
 
from breezy import (
 
25
from brzlib import (
24
26
    branch as _mod_branch,
25
 
    cache_utf8,
 
27
    osutils,
26
28
    revision,
 
29
    symbol_versioning,
27
30
    workingtree,
28
31
    )
29
 
from breezy.i18n import gettext
 
32
from brzlib.i18n import gettext
30
33
""")
31
34
 
32
 
from . import (
 
35
from brzlib import (
33
36
    errors,
34
37
    lazy_regex,
35
38
    registry,
68
71
        if not self._has_revno and self.rev_id is not None:
69
72
            try:
70
73
                self._revno = self.branch.revision_id_to_revno(self.rev_id)
71
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
74
            except errors.NoSuchRevision:
72
75
                self._revno = None
73
76
            self._has_revno = True
74
77
        return self._revno
75
78
 
76
 
    def __bool__(self):
 
79
    def __nonzero__(self):
77
80
        if self.rev_id is None:
78
81
            return False
79
82
        # TODO: otherwise, it should depend on how I was built -
81
84
        # if it's in_store(branch), do the check below
82
85
        return self.branch.repository.has_revision(self.rev_id)
83
86
 
84
 
    __nonzero__ = __bool__
85
 
 
86
87
    def __len__(self):
87
88
        return 2
88
89
 
89
90
    def __getitem__(self, index):
90
 
        if index == 0:
91
 
            return self.revno
92
 
        if index == 1:
93
 
            return self.rev_id
 
91
        if index == 0: return self.revno
 
92
        if index == 1: return self.rev_id
94
93
        raise IndexError(index)
95
94
 
96
95
    def get(self):
99
98
    def __eq__(self, other):
100
99
        if type(other) not in (tuple, list, type(self)):
101
100
            return False
102
 
        if isinstance(other, type(self)) and self.branch is not other.branch:
 
101
        if type(other) is type(self) and self.branch is not other.branch:
103
102
            return False
104
103
        return tuple(self) == tuple(other)
105
104
 
106
105
    def __repr__(self):
107
 
        return '<breezy.revisionspec.RevisionInfo object %s, %s for %r>' % (
 
106
        return '<brzlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
108
107
            self.revno, self.rev_id, self.branch)
109
108
 
110
109
    @staticmethod
111
 
    def from_revision_id(branch, revision_id):
 
110
    def from_revision_id(branch, revision_id, revs=symbol_versioning.DEPRECATED_PARAMETER):
112
111
        """Construct a RevisionInfo given just the id.
113
112
 
114
113
        Use this if you don't know or care what the revno is.
115
114
        """
 
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)
116
120
        return RevisionInfo(branch, revno=None, rev_id=revision_id)
117
121
 
118
122
 
136
140
    """
137
141
 
138
142
    prefix = None
 
143
    # wants_revision_history has been deprecated in 2.5.
 
144
    wants_revision_history = False
139
145
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
140
146
    """Exceptions that RevisionSpec_dwim._match_on will catch.
141
147
 
153
159
        :return: A RevisionSpec object that understands how to parse the
154
160
            supplied notation.
155
161
        """
 
162
        if not isinstance(spec, (type(None), basestring)):
 
163
            raise TypeError('error')
 
164
 
156
165
        if spec is None:
157
166
            return RevisionSpec(None, _internal=True)
158
 
        if not isinstance(spec, str):
159
 
            raise TypeError("revision spec needs to be text")
160
167
        match = revspec_registry.get_prefix(spec)
161
168
        if match is not None:
162
169
            spectype, specsuffix = match
176
183
            called directly. Only from RevisionSpec.from_string()
177
184
        """
178
185
        if not _internal:
179
 
            raise AssertionError(
180
 
                'Creating a RevisionSpec directly is not supported. '
181
 
                'Use RevisionSpec.from_string() instead.')
 
186
            symbol_versioning.warn('Creating a RevisionSpec directly has'
 
187
                                   ' been deprecated in version 0.11. Use'
 
188
                                   ' RevisionSpec.from_string()'
 
189
                                   ' instead.',
 
190
                                   DeprecationWarning, stacklevel=2)
182
191
        self.user_spec = spec
183
192
        if self.prefix and spec.startswith(self.prefix):
184
193
            spec = spec[len(self.prefix):]
201
210
            raise errors.InvalidRevisionSpec(self.spec, branch)
202
211
 
203
212
    def in_history(self, branch):
204
 
        return self._match_on_and_check(branch, revs=None)
 
213
        if branch:
 
214
            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()
 
227
            else:
 
228
                revs = None
 
229
        else:
 
230
            # this should never trigger.
 
231
            # TODO: make it a deprecated code path. RBC 20060928
 
232
            revs = None
 
233
        return self._match_on_and_check(branch, revs)
205
234
 
206
235
        # FIXME: in_history is somewhat broken,
207
236
        # it will return non-history revisions in many
251
280
    def __repr__(self):
252
281
        # this is mostly for helping with testing
253
282
        return '<%s %s>' % (self.__class__.__name__,
254
 
                            self.user_spec)
 
283
                              self.user_spec)
255
284
 
256
285
    def needs_branch(self):
257
286
        """Whether this revision spec needs a branch.
310
339
            except rs_class.dwim_catchable_exceptions:
311
340
                pass
312
341
 
 
342
        # Try the old (deprecated) dwim list:
 
343
        for rs_class in dwim_revspecs:
 
344
            try:
 
345
                return self._try_spectype(rs_class, branch)
 
346
            except rs_class.dwim_catchable_exceptions:
 
347
                pass
 
348
 
313
349
        # Well, I dunno what it is. Note that we don't try to keep track of the
314
350
        # first of last exception raised during the DWIM tries as none seems
315
351
        # really relevant.
369
405
            branch_spec = None
370
406
        else:
371
407
            revno_spec = self.spec[:loc]
372
 
            branch_spec = self.spec[loc + 1:]
 
408
            branch_spec = self.spec[loc+1:]
373
409
 
374
410
        if revno_spec == '':
375
411
            if not branch_spec:
376
412
                raise errors.InvalidRevisionSpec(self.user_spec,
377
 
                                                 branch, 'cannot have an empty revno and no branch')
 
413
                        branch, 'cannot have an empty revno and no branch')
378
414
            revno = None
379
415
        else:
380
416
            try:
385
421
                # but the from_string method is a little primitive
386
422
                # right now - RBC 20060928
387
423
                try:
388
 
                    match_revno = tuple((int(number)
389
 
                                         for number in revno_spec.split('.')))
390
 
                except ValueError as e:
 
424
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
 
425
                except ValueError, e:
391
426
                    raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
392
427
 
393
428
                dotted = True
399
434
        if dotted:
400
435
            try:
401
436
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
402
 
                                                                 _cache_reverse=True)
403
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
437
                    _cache_reverse=True)
 
438
            except errors.NoSuchRevision:
404
439
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
405
440
            else:
406
441
                # there is no traditional 'revno' for dotted-decimal revnos.
417
452
                    revno = last_revno + revno + 1
418
453
            try:
419
454
                revision_id = branch.get_rev_id(revno)
420
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
455
            except errors.NoSuchRevision:
421
456
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
422
457
        return branch, revno, revision_id
423
458
 
433
468
        if self.spec.find(':') == -1:
434
469
            return None
435
470
        else:
436
 
            return self.spec[self.spec.find(':') + 1:]
437
 
 
 
471
            return self.spec[self.spec.find(':')+1:]
438
472
 
439
473
# Old compatibility
440
474
RevisionSpec_int = RevisionSpec_revno
466
500
        # self.spec comes straight from parsing the command line arguments,
467
501
        # so we expect it to be a Unicode string. Switch it to the internal
468
502
        # representation.
469
 
        if isinstance(self.spec, str):
470
 
            return cache_utf8.encode(self.spec)
471
 
        return self.spec
 
503
        return osutils.safe_revision_id(self.spec, warn=False)
 
504
 
472
505
 
473
506
 
474
507
class RevisionSpec_last(RevisionSpec):
500
533
 
501
534
        try:
502
535
            offset = int(self.spec)
503
 
        except ValueError as e:
 
536
        except ValueError, e:
504
537
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
505
538
 
506
539
        if offset <= 0:
510
543
        revno = last_revno - offset + 1
511
544
        try:
512
545
            revision_id = context_branch.get_rev_id(revno)
513
 
        except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
546
        except errors.NoSuchRevision:
514
547
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
515
548
        return revno, revision_id
516
549
 
521
554
        return revision_id
522
555
 
523
556
 
 
557
 
524
558
class RevisionSpec_before(RevisionSpec):
525
559
    """Selects the parent of the revision specified."""
526
560
 
549
583
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
550
584
        if r.revno == 0:
551
585
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
552
 
                                             'cannot go before the null: revision')
 
586
                                         'cannot go before the null: revision')
553
587
        if r.revno is None:
554
588
            # We need to use the repository history here
555
589
            rev = branch.repository.get_revision(r.rev_id)
562
596
            revno = r.revno - 1
563
597
            try:
564
598
                revision_id = branch.get_rev_id(revno, revs)
565
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
599
            except errors.NoSuchRevision:
566
600
                raise errors.InvalidRevisionSpec(self.user_spec,
567
601
                                                 branch)
568
602
        return RevisionInfo(branch, revno, revision_id)
569
603
 
570
604
    def _as_revision_id(self, context_branch):
571
 
        base_revision_id = RevisionSpec.from_string(
572
 
            self.spec)._as_revision_id(context_branch)
 
605
        base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
573
606
        if base_revision_id == revision.NULL_REVISION:
574
607
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
575
 
                                             'cannot go before the null: revision')
 
608
                                         'cannot go before the null: revision')
576
609
        context_repo = context_branch.repository
577
 
        with context_repo.lock_read():
 
610
        context_repo.lock_read()
 
611
        try:
578
612
            parent_map = context_repo.get_parent_map([base_revision_id])
 
613
        finally:
 
614
            context_repo.unlock()
579
615
        if base_revision_id not in parent_map:
580
616
            # Ghost, or unknown revision id
581
617
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
582
 
                                             'cannot find the matching revision')
 
618
                'cannot find the matching revision')
583
619
        parents = parent_map[base_revision_id]
584
620
        if len(parents) < 1:
585
621
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
586
 
                                             'No parents for revision.')
 
622
                'No parents for revision.')
587
623
        return parents[0]
588
624
 
589
625
 
 
626
 
590
627
class RevisionSpec_tag(RevisionSpec):
591
628
    """Select a revision identified by tag name"""
592
629
 
601
638
    def _match_on(self, branch, revs):
602
639
        # Can raise tags not supported, NoSuchTag, etc
603
640
        return RevisionInfo.from_revision_id(branch,
604
 
                                             branch.tags.lookup_tag(self.spec))
 
641
            branch.tags.lookup_tag(self.spec))
605
642
 
606
643
    def _as_revision_id(self, context_branch):
607
644
        return context_branch.tags.lookup_tag(self.spec)
608
645
 
609
646
 
 
647
 
610
648
class _RevListToTimestamps(object):
611
649
    """This takes a list of revisions, and allows you to bisect by date"""
612
650
 
637
675
 
638
676
    One way to display all the changes since yesterday would be::
639
677
 
640
 
        brz log -r date:yesterday..
 
678
        bzr log -r date:yesterday..
641
679
 
642
680
    Examples::
643
681
 
647
685
    """
648
686
    prefix = 'date:'
649
687
    _date_regex = lazy_regex.lazy_compile(
650
 
        r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
651
 
        r'(,|T)?\s*'
652
 
        r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
 
688
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
 
689
            r'(,|T)?\s*'
 
690
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
653
691
        )
654
692
 
655
693
    def _match_on(self, branch, revs):
662
700
        #  XXX: This doesn't actually work
663
701
        #  So the proper way of saying 'give me all entries for today' is:
664
702
        #      -r date:yesterday..date:today
665
 
        today = datetime.datetime.fromordinal(
666
 
            datetime.date.today().toordinal())
 
703
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
667
704
        if self.spec.lower() == 'yesterday':
668
705
            dt = today - datetime.timedelta(days=1)
669
706
        elif self.spec.lower() == 'today':
694
731
                    else:
695
732
                        second = 0
696
733
                else:
697
 
                    hour, minute, second = 0, 0, 0
 
734
                    hour, minute, second = 0,0,0
698
735
            except ValueError:
699
736
                raise errors.InvalidRevisionSpec(self.user_spec,
700
737
                                                 branch, 'invalid date')
701
738
 
702
739
            dt = datetime.datetime(year=year, month=month, day=day,
703
 
                                   hour=hour, minute=minute, second=second)
704
 
        with branch.lock_read():
 
740
                    hour=hour, minute=minute, second=second)
 
741
        branch.lock_read()
 
742
        try:
705
743
            rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
 
744
        finally:
 
745
            branch.unlock()
706
746
        if rev == branch.revno():
707
747
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
708
748
        return RevisionInfo(branch, rev)
709
749
 
710
750
 
 
751
 
711
752
class RevisionSpec_ancestor(RevisionSpec):
712
753
    """Selects a common ancestor with a second branch."""
713
754
 
745
786
 
746
787
    @staticmethod
747
788
    def _find_revision_id(branch, other_location):
748
 
        from .branch import Branch
 
789
        from brzlib.branch import Branch
749
790
 
750
 
        with branch.lock_read():
 
791
        branch.lock_read()
 
792
        try:
751
793
            revision_a = revision.ensure_null(branch.last_revision())
752
794
            if revision_a == revision.NULL_REVISION:
753
795
                raise errors.NoCommits(branch)
754
796
            if other_location == '':
755
797
                other_location = branch.get_parent()
756
798
            other_branch = Branch.open(other_location)
757
 
            with other_branch.lock_read():
 
799
            other_branch.lock_read()
 
800
            try:
758
801
                revision_b = revision.ensure_null(other_branch.last_revision())
759
802
                if revision_b == revision.NULL_REVISION:
760
803
                    raise errors.NoCommits(other_branch)
761
804
                graph = branch.repository.get_graph(other_branch.repository)
762
805
                rev_id = graph.find_unique_lca(revision_a, revision_b)
 
806
            finally:
 
807
                other_branch.unlock()
763
808
            if rev_id == revision.NULL_REVISION:
764
809
                raise errors.NoCommonAncestor(revision_a, revision_b)
765
810
            return rev_id
 
811
        finally:
 
812
            branch.unlock()
 
813
 
 
814
 
766
815
 
767
816
 
768
817
class RevisionSpec_branch(RevisionSpec):
780
829
    dwim_catchable_exceptions = (errors.NotBranchError,)
781
830
 
782
831
    def _match_on(self, branch, revs):
783
 
        from .branch import Branch
 
832
        from brzlib.branch import Branch
784
833
        other_branch = Branch.open(self.spec)
785
834
        revision_b = other_branch.last_revision()
786
835
        if revision_b in (None, revision.NULL_REVISION):
796
845
        return RevisionInfo(branch, None, revision_b)
797
846
 
798
847
    def _as_revision_id(self, context_branch):
799
 
        from .branch import Branch
 
848
        from brzlib.branch import Branch
800
849
        other_branch = Branch.open(self.spec)
801
850
        last_revision = other_branch.last_revision()
802
851
        last_revision = revision.ensure_null(last_revision)
806
855
        return last_revision
807
856
 
808
857
    def _as_tree(self, context_branch):
809
 
        from .branch import Branch
 
858
        from brzlib.branch import Branch
810
859
        other_branch = Branch.open(self.spec)
811
860
        last_revision = other_branch.last_revision()
812
861
        last_revision = revision.ensure_null(last_revision)
821
870
        return self.spec
822
871
 
823
872
 
 
873
 
824
874
class RevisionSpec_submit(RevisionSpec_ancestor):
825
875
    """Selects a common ancestor with a submit branch."""
826
876
 
851
901
        if submit_location is None:
852
902
            raise errors.NoSubmitBranch(branch)
853
903
        trace.note(gettext('Using {0} {1}').format(location_type,
854
 
                                                   submit_location))
 
904
                                                        submit_location))
855
905
        return submit_location
856
906
 
857
907
    def _match_on(self, branch, revs):
858
908
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
859
909
        return self._find_revision_info(branch,
860
 
                                        self._get_submit_location(branch))
 
910
            self._get_submit_location(branch))
861
911
 
862
912
    def _as_revision_id(self, context_branch):
863
913
        return self._find_revision_id(context_branch,
864
 
                                      self._get_submit_location(context_branch))
 
914
            self._get_submit_location(context_branch))
865
915
 
866
916
 
867
917
class RevisionSpec_annotate(RevisionIDSpec):
878
928
 
879
929
    def _raise_invalid(self, numstring, context_branch):
880
930
        raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
881
 
                                         'No such line: %s' % numstring)
 
931
            'No such line: %s' % numstring)
882
932
 
883
933
    def _as_revision_id(self, context_branch):
884
934
        path, numstring = self.spec.rsplit(':', 1)
887
937
        except ValueError:
888
938
            self._raise_invalid(numstring, context_branch)
889
939
        tree, file_path = workingtree.WorkingTree.open_containing(path)
890
 
        with tree.lock_read():
891
 
            if not tree.has_filename(file_path):
 
940
        tree.lock_read()
 
941
        try:
 
942
            file_id = tree.path2id(file_path)
 
943
            if file_id is None:
892
944
                raise errors.InvalidRevisionSpec(self.user_spec,
893
 
                                                 context_branch, "File '%s' is not versioned." %
894
 
                                                 file_path)
895
 
            revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
 
945
                    context_branch, "File '%s' is not versioned." %
 
946
                    file_path)
 
947
            revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
 
948
        finally:
 
949
            tree.unlock()
896
950
        try:
897
951
            revision_id = revision_ids[index]
898
952
        except IndexError:
899
953
            self._raise_invalid(numstring, context_branch)
900
954
        if revision_id == revision.CURRENT_REVISION:
901
955
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
902
 
                                             'Line %s has not been committed.' % numstring)
 
956
                'Line %s has not been committed.' % numstring)
903
957
        return revision_id
904
958
 
905
959
 
930
984
# The order in which we want to DWIM a revision spec without any prefix.
931
985
# revno is always tried first and isn't listed here, this is used by
932
986
# RevisionSpec_dwim._match_on
 
987
dwim_revspecs = symbol_versioning.deprecated_list(
 
988
    symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
 
989
 
933
990
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
934
991
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
935
992
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
936
993
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
937
994
 
938
995
revspec_registry = registry.Registry()
939
 
 
940
 
 
941
996
def _register_revspec(revspec):
942
997
    revspec_registry.register(revspec.prefix, revspec)
943
998
 
944
 
 
945
999
_register_revspec(RevisionSpec_revno)
946
1000
_register_revspec(RevisionSpec_revid)
947
1001
_register_revspec(RevisionSpec_last)