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

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

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
 
17
19
 
18
20
from .lazy_import import lazy_import
19
21
lazy_import(globals(), """
23
25
from breezy import (
24
26
    branch as _mod_branch,
25
27
    cache_utf8,
 
28
    osutils,
26
29
    revision,
27
30
    workingtree,
28
31
    )
37
40
    )
38
41
 
39
42
 
40
 
class InvalidRevisionSpec(errors.BzrError):
41
 
 
42
 
    _fmt = ("Requested revision: '%(spec)s' does not exist in branch:"
43
 
            " %(branch_url)s%(extra)s")
44
 
 
45
 
    def __init__(self, spec, branch, extra=None):
46
 
        errors.BzrError.__init__(self, branch=branch, spec=spec)
47
 
        self.branch_url = getattr(branch, 'user_url', str(branch))
48
 
        if extra:
49
 
            self.extra = '\n' + str(extra)
50
 
        else:
51
 
            self.extra = ''
52
 
 
53
 
 
54
 
class InvalidRevisionSpec(errors.BzrError):
55
 
 
56
 
    _fmt = ("Requested revision: '%(spec)s' does not exist in branch:"
57
 
            " %(branch_url)s%(extra)s")
58
 
 
59
 
    def __init__(self, spec, branch, extra=None):
60
 
        errors.BzrError.__init__(self, branch=branch, spec=spec)
61
 
        self.branch_url = getattr(branch, 'user_url', str(branch))
62
 
        if extra:
63
 
            self.extra = '\n' + str(extra)
64
 
        else:
65
 
            self.extra = ''
66
 
 
67
 
 
68
43
class RevisionInfo(object):
69
44
    """The results of applying a revision specification to a branch."""
70
45
 
96
71
        if not self._has_revno and self.rev_id is not None:
97
72
            try:
98
73
                self._revno = self.branch.revision_id_to_revno(self.rev_id)
99
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
74
            except errors.NoSuchRevision:
100
75
                self._revno = None
101
76
            self._has_revno = True
102
77
        return self._revno
115
90
        return 2
116
91
 
117
92
    def __getitem__(self, index):
118
 
        if index == 0:
119
 
            return self.revno
120
 
        if index == 1:
121
 
            return self.rev_id
 
93
        if index == 0: return self.revno
 
94
        if index == 1: return self.rev_id
122
95
        raise IndexError(index)
123
96
 
124
97
    def get(self):
164
137
    """
165
138
 
166
139
    prefix = None
167
 
    dwim_catchable_exceptions = (InvalidRevisionSpec,)
 
140
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
168
141
    """Exceptions that RevisionSpec_dwim._match_on will catch.
169
142
 
170
143
    If the revspec is part of ``dwim_revspecs``, it may be tried with an
181
154
        :return: A RevisionSpec object that understands how to parse the
182
155
            supplied notation.
183
156
        """
 
157
        if not isinstance(spec, (type(None), basestring)):
 
158
            raise TypeError('error')
 
159
 
184
160
        if spec is None:
185
161
            return RevisionSpec(None, _internal=True)
186
 
        if not isinstance(spec, str):
187
 
            raise TypeError("revision spec needs to be text")
188
162
        match = revspec_registry.get_prefix(spec)
189
163
        if match is not None:
190
164
            spectype, specsuffix = match
224
198
            # special case - nothing supplied
225
199
            return info
226
200
        elif self.prefix:
227
 
            raise InvalidRevisionSpec(self.user_spec, branch)
 
201
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
228
202
        else:
229
 
            raise InvalidRevisionSpec(self.spec, branch)
 
203
            raise errors.InvalidRevisionSpec(self.spec, branch)
230
204
 
231
205
    def in_history(self, branch):
232
206
        return self._match_on_and_check(branch, revs=None)
279
253
    def __repr__(self):
280
254
        # this is mostly for helping with testing
281
255
        return '<%s %s>' % (self.__class__.__name__,
282
 
                            self.user_spec)
 
256
                              self.user_spec)
283
257
 
284
258
    def needs_branch(self):
285
259
        """Whether this revision spec needs a branch.
341
315
        # Well, I dunno what it is. Note that we don't try to keep track of the
342
316
        # first of last exception raised during the DWIM tries as none seems
343
317
        # really relevant.
344
 
        raise InvalidRevisionSpec(self.spec, branch)
 
318
        raise errors.InvalidRevisionSpec(self.spec, branch)
345
319
 
346
320
    @classmethod
347
321
    def append_possible_revspec(cls, revspec):
397
371
            branch_spec = None
398
372
        else:
399
373
            revno_spec = self.spec[:loc]
400
 
            branch_spec = self.spec[loc + 1:]
 
374
            branch_spec = self.spec[loc+1:]
401
375
 
402
376
        if revno_spec == '':
403
377
            if not branch_spec:
404
 
                raise InvalidRevisionSpec(
405
 
                    self.user_spec, branch,
406
 
                    'cannot have an empty revno and no branch')
 
378
                raise errors.InvalidRevisionSpec(self.user_spec,
 
379
                        branch, 'cannot have an empty revno and no branch')
407
380
            revno = None
408
381
        else:
409
382
            try:
414
387
                # but the from_string method is a little primitive
415
388
                # right now - RBC 20060928
416
389
                try:
417
 
                    match_revno = tuple((int(number)
418
 
                                         for number in revno_spec.split('.')))
 
390
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
419
391
                except ValueError as e:
420
 
                    raise InvalidRevisionSpec(self.user_spec, branch, e)
 
392
                    raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
421
393
 
422
394
                dotted = True
423
395
 
428
400
        if dotted:
429
401
            try:
430
402
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
431
 
                                                                 _cache_reverse=True)
432
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
433
 
                raise InvalidRevisionSpec(self.user_spec, branch)
 
403
                    _cache_reverse=True)
 
404
            except errors.NoSuchRevision:
 
405
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
434
406
            else:
435
407
                # there is no traditional 'revno' for dotted-decimal revnos.
436
408
                # so for API compatibility we return None.
446
418
                    revno = last_revno + revno + 1
447
419
            try:
448
420
                revision_id = branch.get_rev_id(revno)
449
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
450
 
                raise InvalidRevisionSpec(self.user_spec, branch)
 
421
            except errors.NoSuchRevision:
 
422
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
451
423
        return branch, revno, revision_id
452
424
 
453
425
    def _as_revision_id(self, context_branch):
462
434
        if self.spec.find(':') == -1:
463
435
            return None
464
436
        else:
465
 
            return self.spec[self.spec.find(':') + 1:]
466
 
 
 
437
            return self.spec[self.spec.find(':')+1:]
467
438
 
468
439
# Old compatibility
469
440
RevisionSpec_int = RevisionSpec_revno
495
466
        # self.spec comes straight from parsing the command line arguments,
496
467
        # so we expect it to be a Unicode string. Switch it to the internal
497
468
        # representation.
498
 
        if isinstance(self.spec, str):
 
469
        if isinstance(self.spec, unicode):
499
470
            return cache_utf8.encode(self.spec)
500
471
        return self.spec
501
472
 
502
473
 
 
474
 
503
475
class RevisionSpec_last(RevisionSpec):
504
476
    """Selects the nth revision from the end."""
505
477
 
530
502
        try:
531
503
            offset = int(self.spec)
532
504
        except ValueError as e:
533
 
            raise InvalidRevisionSpec(self.user_spec, context_branch, e)
 
505
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
534
506
 
535
507
        if offset <= 0:
536
 
            raise InvalidRevisionSpec(
537
 
                self.user_spec, context_branch,
538
 
                'you must supply a positive value')
 
508
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
509
                                             'you must supply a positive value')
539
510
 
540
511
        revno = last_revno - offset + 1
541
512
        try:
542
513
            revision_id = context_branch.get_rev_id(revno)
543
 
        except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
544
 
            raise InvalidRevisionSpec(self.user_spec, context_branch)
 
514
        except errors.NoSuchRevision:
 
515
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
545
516
        return revno, revision_id
546
517
 
547
518
    def _as_revision_id(self, context_branch):
551
522
        return revision_id
552
523
 
553
524
 
 
525
 
554
526
class RevisionSpec_before(RevisionSpec):
555
527
    """Selects the parent of the revision specified."""
556
528
 
578
550
    def _match_on(self, branch, revs):
579
551
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
580
552
        if r.revno == 0:
581
 
            raise InvalidRevisionSpec(
582
 
                self.user_spec, branch,
583
 
                'cannot go before the null: revision')
 
553
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
 
554
                                         'cannot go before the null: revision')
584
555
        if r.revno is None:
585
556
            # We need to use the repository history here
586
557
            rev = branch.repository.get_revision(r.rev_id)
593
564
            revno = r.revno - 1
594
565
            try:
595
566
                revision_id = branch.get_rev_id(revno, revs)
596
 
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
597
 
                raise InvalidRevisionSpec(self.user_spec, branch)
 
567
            except errors.NoSuchRevision:
 
568
                raise errors.InvalidRevisionSpec(self.user_spec,
 
569
                                                 branch)
598
570
        return RevisionInfo(branch, revno, revision_id)
599
571
 
600
572
    def _as_revision_id(self, context_branch):
601
 
        base_revision_id = RevisionSpec.from_string(
602
 
            self.spec)._as_revision_id(context_branch)
 
573
        base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
603
574
        if base_revision_id == revision.NULL_REVISION:
604
 
            raise InvalidRevisionSpec(
605
 
                self.user_spec, context_branch,
606
 
                'cannot go before the null: revision')
 
575
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
576
                                         'cannot go before the null: revision')
607
577
        context_repo = context_branch.repository
608
 
        with context_repo.lock_read():
 
578
        context_repo.lock_read()
 
579
        try:
609
580
            parent_map = context_repo.get_parent_map([base_revision_id])
 
581
        finally:
 
582
            context_repo.unlock()
610
583
        if base_revision_id not in parent_map:
611
584
            # Ghost, or unknown revision id
612
 
            raise InvalidRevisionSpec(
613
 
                self.user_spec, context_branch, 'cannot find the matching revision')
 
585
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
586
                'cannot find the matching revision')
614
587
        parents = parent_map[base_revision_id]
615
588
        if len(parents) < 1:
616
 
            raise InvalidRevisionSpec(
617
 
                self.user_spec, context_branch, 'No parents for revision.')
 
589
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
590
                'No parents for revision.')
618
591
        return parents[0]
619
592
 
620
593
 
 
594
 
621
595
class RevisionSpec_tag(RevisionSpec):
622
596
    """Select a revision identified by tag name"""
623
597
 
632
606
    def _match_on(self, branch, revs):
633
607
        # Can raise tags not supported, NoSuchTag, etc
634
608
        return RevisionInfo.from_revision_id(branch,
635
 
                                             branch.tags.lookup_tag(self.spec))
 
609
            branch.tags.lookup_tag(self.spec))
636
610
 
637
611
    def _as_revision_id(self, context_branch):
638
612
        return context_branch.tags.lookup_tag(self.spec)
639
613
 
640
614
 
 
615
 
641
616
class _RevListToTimestamps(object):
642
617
    """This takes a list of revisions, and allows you to bisect by date"""
643
618
 
678
653
    """
679
654
    prefix = 'date:'
680
655
    _date_regex = lazy_regex.lazy_compile(
681
 
        r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
682
 
        r'(,|T)?\s*'
683
 
        r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
 
656
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
 
657
            r'(,|T)?\s*'
 
658
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
684
659
        )
685
660
 
686
661
    def _match_on(self, branch, revs):
693
668
        #  XXX: This doesn't actually work
694
669
        #  So the proper way of saying 'give me all entries for today' is:
695
670
        #      -r date:yesterday..date:today
696
 
        today = datetime.datetime.fromordinal(
697
 
            datetime.date.today().toordinal())
 
671
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
698
672
        if self.spec.lower() == 'yesterday':
699
673
            dt = today - datetime.timedelta(days=1)
700
674
        elif self.spec.lower() == 'today':
704
678
        else:
705
679
            m = self._date_regex.match(self.spec)
706
680
            if not m or (not m.group('date') and not m.group('time')):
707
 
                raise InvalidRevisionSpec(
708
 
                    self.user_spec, branch, 'invalid date')
 
681
                raise errors.InvalidRevisionSpec(self.user_spec,
 
682
                                                 branch, 'invalid date')
709
683
 
710
684
            try:
711
685
                if m.group('date'):
725
699
                    else:
726
700
                        second = 0
727
701
                else:
728
 
                    hour, minute, second = 0, 0, 0
 
702
                    hour, minute, second = 0,0,0
729
703
            except ValueError:
730
 
                raise InvalidRevisionSpec(
731
 
                    self.user_spec, branch, 'invalid date')
 
704
                raise errors.InvalidRevisionSpec(self.user_spec,
 
705
                                                 branch, 'invalid date')
732
706
 
733
707
            dt = datetime.datetime(year=year, month=month, day=day,
734
 
                                   hour=hour, minute=minute, second=second)
735
 
        with branch.lock_read():
 
708
                    hour=hour, minute=minute, second=second)
 
709
        branch.lock_read()
 
710
        try:
736
711
            rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
 
712
        finally:
 
713
            branch.unlock()
737
714
        if rev == branch.revno():
738
 
            raise InvalidRevisionSpec(self.user_spec, branch)
 
715
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
739
716
        return RevisionInfo(branch, rev)
740
717
 
741
718
 
 
719
 
742
720
class RevisionSpec_ancestor(RevisionSpec):
743
721
    """Selects a common ancestor with a second branch."""
744
722
 
778
756
    def _find_revision_id(branch, other_location):
779
757
        from .branch import Branch
780
758
 
781
 
        with branch.lock_read():
 
759
        branch.lock_read()
 
760
        try:
782
761
            revision_a = revision.ensure_null(branch.last_revision())
783
762
            if revision_a == revision.NULL_REVISION:
784
763
                raise errors.NoCommits(branch)
785
764
            if other_location == '':
786
765
                other_location = branch.get_parent()
787
766
            other_branch = Branch.open(other_location)
788
 
            with other_branch.lock_read():
 
767
            other_branch.lock_read()
 
768
            try:
789
769
                revision_b = revision.ensure_null(other_branch.last_revision())
790
770
                if revision_b == revision.NULL_REVISION:
791
771
                    raise errors.NoCommits(other_branch)
792
772
                graph = branch.repository.get_graph(other_branch.repository)
793
773
                rev_id = graph.find_unique_lca(revision_a, revision_b)
 
774
            finally:
 
775
                other_branch.unlock()
794
776
            if rev_id == revision.NULL_REVISION:
795
777
                raise errors.NoCommonAncestor(revision_a, revision_b)
796
778
            return rev_id
 
779
        finally:
 
780
            branch.unlock()
 
781
 
 
782
 
797
783
 
798
784
 
799
785
class RevisionSpec_branch(RevisionSpec):
852
838
        return self.spec
853
839
 
854
840
 
 
841
 
855
842
class RevisionSpec_submit(RevisionSpec_ancestor):
856
843
    """Selects a common ancestor with a submit branch."""
857
844
 
882
869
        if submit_location is None:
883
870
            raise errors.NoSubmitBranch(branch)
884
871
        trace.note(gettext('Using {0} {1}').format(location_type,
885
 
                                                   submit_location))
 
872
                                                        submit_location))
886
873
        return submit_location
887
874
 
888
875
    def _match_on(self, branch, revs):
889
876
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
890
877
        return self._find_revision_info(branch,
891
 
                                        self._get_submit_location(branch))
 
878
            self._get_submit_location(branch))
892
879
 
893
880
    def _as_revision_id(self, context_branch):
894
881
        return self._find_revision_id(context_branch,
895
 
                                      self._get_submit_location(context_branch))
 
882
            self._get_submit_location(context_branch))
896
883
 
897
884
 
898
885
class RevisionSpec_annotate(RevisionIDSpec):
908
895
    """
909
896
 
910
897
    def _raise_invalid(self, numstring, context_branch):
911
 
        raise InvalidRevisionSpec(
912
 
            self.user_spec, context_branch,
 
898
        raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
913
899
            'No such line: %s' % numstring)
914
900
 
915
901
    def _as_revision_id(self, context_branch):
919
905
        except ValueError:
920
906
            self._raise_invalid(numstring, context_branch)
921
907
        tree, file_path = workingtree.WorkingTree.open_containing(path)
922
 
        with tree.lock_read():
923
 
            if not tree.has_filename(file_path):
924
 
                raise InvalidRevisionSpec(
925
 
                    self.user_spec, context_branch,
926
 
                    "File '%s' is not versioned." % file_path)
927
 
            revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
 
908
        tree.lock_read()
 
909
        try:
 
910
            file_id = tree.path2id(file_path)
 
911
            if file_id is None:
 
912
                raise errors.InvalidRevisionSpec(self.user_spec,
 
913
                    context_branch, "File '%s' is not versioned." %
 
914
                    file_path)
 
915
            revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
 
916
        finally:
 
917
            tree.unlock()
928
918
        try:
929
919
            revision_id = revision_ids[index]
930
920
        except IndexError:
931
921
            self._raise_invalid(numstring, context_branch)
932
922
        if revision_id == revision.CURRENT_REVISION:
933
 
            raise InvalidRevisionSpec(
934
 
                self.user_spec, context_branch,
 
923
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
935
924
                'Line %s has not been committed.' % numstring)
936
925
        return revision_id
937
926
 
956
945
        result = graph.find_lefthand_merger(revision_id,
957
946
                                            context_branch.last_revision())
958
947
        if result is None:
959
 
            raise InvalidRevisionSpec(self.user_spec, context_branch)
 
948
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
960
949
        return result
961
950
 
962
951
 
969
958
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
970
959
 
971
960
revspec_registry = registry.Registry()
972
 
 
973
 
 
974
961
def _register_revspec(revspec):
975
962
    revspec_registry.register(revspec.prefix, revspec)
976
963
 
977
 
 
978
964
_register_revspec(RevisionSpec_revno)
979
965
_register_revspec(RevisionSpec_revid)
980
966
_register_revspec(RevisionSpec_last)