/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: 2020-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

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 .lazy_import import lazy_import
21
19
lazy_import(globals(), """
25
23
from breezy import (
26
24
    branch as _mod_branch,
27
25
    cache_utf8,
28
 
    osutils,
29
26
    revision,
30
27
    workingtree,
31
28
    )
38
35
    registry,
39
36
    trace,
40
37
    )
41
 
from .sixish import (
42
 
    text_type,
43
 
    )
 
38
 
 
39
 
 
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 = ''
44
52
 
45
53
 
46
54
class RevisionInfo(object):
74
82
        if not self._has_revno and self.rev_id is not None:
75
83
            try:
76
84
                self._revno = self.branch.revision_id_to_revno(self.rev_id)
77
 
            except errors.NoSuchRevision:
 
85
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
78
86
                self._revno = None
79
87
            self._has_revno = True
80
88
        return self._revno
93
101
        return 2
94
102
 
95
103
    def __getitem__(self, index):
96
 
        if index == 0: return self.revno
97
 
        if index == 1: return self.rev_id
 
104
        if index == 0:
 
105
            return self.revno
 
106
        if index == 1:
 
107
            return self.rev_id
98
108
        raise IndexError(index)
99
109
 
100
110
    def get(self):
140
150
    """
141
151
 
142
152
    prefix = None
143
 
    dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
 
153
    dwim_catchable_exceptions = (InvalidRevisionSpec,)
144
154
    """Exceptions that RevisionSpec_dwim._match_on will catch.
145
155
 
146
156
    If the revspec is part of ``dwim_revspecs``, it may be tried with an
159
169
        """
160
170
        if spec is None:
161
171
            return RevisionSpec(None, _internal=True)
162
 
        if not isinstance(spec, (str, text_type)):
 
172
        if not isinstance(spec, str):
163
173
            raise TypeError("revision spec needs to be text")
164
174
        match = revspec_registry.get_prefix(spec)
165
175
        if match is not None:
200
210
            # special case - nothing supplied
201
211
            return info
202
212
        elif self.prefix:
203
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
213
            raise InvalidRevisionSpec(self.user_spec, branch)
204
214
        else:
205
 
            raise errors.InvalidRevisionSpec(self.spec, branch)
 
215
            raise InvalidRevisionSpec(self.spec, branch)
206
216
 
207
217
    def in_history(self, branch):
208
218
        return self._match_on_and_check(branch, revs=None)
255
265
    def __repr__(self):
256
266
        # this is mostly for helping with testing
257
267
        return '<%s %s>' % (self.__class__.__name__,
258
 
                              self.user_spec)
 
268
                            self.user_spec)
259
269
 
260
270
    def needs_branch(self):
261
271
        """Whether this revision spec needs a branch.
317
327
        # Well, I dunno what it is. Note that we don't try to keep track of the
318
328
        # first of last exception raised during the DWIM tries as none seems
319
329
        # really relevant.
320
 
        raise errors.InvalidRevisionSpec(self.spec, branch)
 
330
        raise InvalidRevisionSpec(self.spec, branch)
321
331
 
322
332
    @classmethod
323
333
    def append_possible_revspec(cls, revspec):
373
383
            branch_spec = None
374
384
        else:
375
385
            revno_spec = self.spec[:loc]
376
 
            branch_spec = self.spec[loc+1:]
 
386
            branch_spec = self.spec[loc + 1:]
377
387
 
378
388
        if revno_spec == '':
379
389
            if not branch_spec:
380
 
                raise errors.InvalidRevisionSpec(self.user_spec,
381
 
                        branch, 'cannot have an empty revno and no branch')
 
390
                raise InvalidRevisionSpec(
 
391
                    self.user_spec, branch,
 
392
                    'cannot have an empty revno and no branch')
382
393
            revno = None
383
394
        else:
384
395
            try:
389
400
                # but the from_string method is a little primitive
390
401
                # right now - RBC 20060928
391
402
                try:
392
 
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
 
403
                    match_revno = tuple((int(number)
 
404
                                         for number in revno_spec.split('.')))
393
405
                except ValueError as e:
394
 
                    raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
 
406
                    raise InvalidRevisionSpec(self.user_spec, branch, e)
395
407
 
396
408
                dotted = True
397
409
 
402
414
        if dotted:
403
415
            try:
404
416
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
405
 
                    _cache_reverse=True)
406
 
            except errors.NoSuchRevision:
407
 
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
417
                                                                 _cache_reverse=True)
 
418
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
419
                raise InvalidRevisionSpec(self.user_spec, branch)
408
420
            else:
409
421
                # there is no traditional 'revno' for dotted-decimal revnos.
410
422
                # so for API compatibility we return None.
420
432
                    revno = last_revno + revno + 1
421
433
            try:
422
434
                revision_id = branch.get_rev_id(revno)
423
 
            except errors.NoSuchRevision:
424
 
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
435
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
436
                raise InvalidRevisionSpec(self.user_spec, branch)
425
437
        return branch, revno, revision_id
426
438
 
427
439
    def _as_revision_id(self, context_branch):
436
448
        if self.spec.find(':') == -1:
437
449
            return None
438
450
        else:
439
 
            return self.spec[self.spec.find(':')+1:]
 
451
            return self.spec[self.spec.find(':') + 1:]
 
452
 
440
453
 
441
454
# Old compatibility
442
455
RevisionSpec_int = RevisionSpec_revno
468
481
        # self.spec comes straight from parsing the command line arguments,
469
482
        # so we expect it to be a Unicode string. Switch it to the internal
470
483
        # representation.
471
 
        if isinstance(self.spec, unicode):
 
484
        if isinstance(self.spec, str):
472
485
            return cache_utf8.encode(self.spec)
473
486
        return self.spec
474
487
 
475
488
 
476
 
 
477
489
class RevisionSpec_last(RevisionSpec):
478
490
    """Selects the nth revision from the end."""
479
491
 
504
516
        try:
505
517
            offset = int(self.spec)
506
518
        except ValueError as e:
507
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
 
519
            raise InvalidRevisionSpec(self.user_spec, context_branch, e)
508
520
 
509
521
        if offset <= 0:
510
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
511
 
                                             'you must supply a positive value')
 
522
            raise InvalidRevisionSpec(
 
523
                self.user_spec, context_branch,
 
524
                'you must supply a positive value')
512
525
 
513
526
        revno = last_revno - offset + 1
514
527
        try:
515
528
            revision_id = context_branch.get_rev_id(revno)
516
 
        except errors.NoSuchRevision:
517
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
 
529
        except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
530
            raise InvalidRevisionSpec(self.user_spec, context_branch)
518
531
        return revno, revision_id
519
532
 
520
533
    def _as_revision_id(self, context_branch):
524
537
        return revision_id
525
538
 
526
539
 
527
 
 
528
540
class RevisionSpec_before(RevisionSpec):
529
541
    """Selects the parent of the revision specified."""
530
542
 
552
564
    def _match_on(self, branch, revs):
553
565
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
554
566
        if r.revno == 0:
555
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
556
 
                                         'cannot go before the null: revision')
 
567
            raise InvalidRevisionSpec(
 
568
                self.user_spec, branch,
 
569
                'cannot go before the null: revision')
557
570
        if r.revno is None:
558
571
            # We need to use the repository history here
559
572
            rev = branch.repository.get_revision(r.rev_id)
566
579
            revno = r.revno - 1
567
580
            try:
568
581
                revision_id = branch.get_rev_id(revno, revs)
569
 
            except errors.NoSuchRevision:
570
 
                raise errors.InvalidRevisionSpec(self.user_spec,
571
 
                                                 branch)
 
582
            except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
 
583
                raise InvalidRevisionSpec(self.user_spec, branch)
572
584
        return RevisionInfo(branch, revno, revision_id)
573
585
 
574
586
    def _as_revision_id(self, context_branch):
575
 
        base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
 
587
        base_revision_id = RevisionSpec.from_string(
 
588
            self.spec)._as_revision_id(context_branch)
576
589
        if base_revision_id == revision.NULL_REVISION:
577
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
578
 
                                         'cannot go before the null: revision')
 
590
            raise InvalidRevisionSpec(
 
591
                self.user_spec, context_branch,
 
592
                'cannot go before the null: revision')
579
593
        context_repo = context_branch.repository
580
 
        context_repo.lock_read()
581
 
        try:
 
594
        with context_repo.lock_read():
582
595
            parent_map = context_repo.get_parent_map([base_revision_id])
583
 
        finally:
584
 
            context_repo.unlock()
585
596
        if base_revision_id not in parent_map:
586
597
            # Ghost, or unknown revision id
587
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
588
 
                'cannot find the matching revision')
 
598
            raise InvalidRevisionSpec(
 
599
                self.user_spec, context_branch, 'cannot find the matching revision')
589
600
        parents = parent_map[base_revision_id]
590
601
        if len(parents) < 1:
591
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
592
 
                'No parents for revision.')
 
602
            raise errors.InvalidRevisionSpec(
 
603
                self.user_spec, context_branch, 'No parents for revision.')
593
604
        return parents[0]
594
605
 
595
606
 
596
 
 
597
607
class RevisionSpec_tag(RevisionSpec):
598
608
    """Select a revision identified by tag name"""
599
609
 
608
618
    def _match_on(self, branch, revs):
609
619
        # Can raise tags not supported, NoSuchTag, etc
610
620
        return RevisionInfo.from_revision_id(branch,
611
 
            branch.tags.lookup_tag(self.spec))
 
621
                                             branch.tags.lookup_tag(self.spec))
612
622
 
613
623
    def _as_revision_id(self, context_branch):
614
624
        return context_branch.tags.lookup_tag(self.spec)
615
625
 
616
626
 
617
 
 
618
627
class _RevListToTimestamps(object):
619
628
    """This takes a list of revisions, and allows you to bisect by date"""
620
629
 
655
664
    """
656
665
    prefix = 'date:'
657
666
    _date_regex = lazy_regex.lazy_compile(
658
 
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
659
 
            r'(,|T)?\s*'
660
 
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
 
667
        r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
 
668
        r'(,|T)?\s*'
 
669
        r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
661
670
        )
662
671
 
663
672
    def _match_on(self, branch, revs):
670
679
        #  XXX: This doesn't actually work
671
680
        #  So the proper way of saying 'give me all entries for today' is:
672
681
        #      -r date:yesterday..date:today
673
 
        today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
 
682
        today = datetime.datetime.fromordinal(
 
683
            datetime.date.today().toordinal())
674
684
        if self.spec.lower() == 'yesterday':
675
685
            dt = today - datetime.timedelta(days=1)
676
686
        elif self.spec.lower() == 'today':
680
690
        else:
681
691
            m = self._date_regex.match(self.spec)
682
692
            if not m or (not m.group('date') and not m.group('time')):
683
 
                raise errors.InvalidRevisionSpec(self.user_spec,
684
 
                                                 branch, 'invalid date')
 
693
                raise InvalidRevisionSpec(
 
694
                    self.user_spec, branch, 'invalid date')
685
695
 
686
696
            try:
687
697
                if m.group('date'):
703
713
                else:
704
714
                    hour, minute, second = 0, 0, 0
705
715
            except ValueError:
706
 
                raise errors.InvalidRevisionSpec(self.user_spec,
707
 
                                                 branch, 'invalid date')
 
716
                raise InvalidRevisionSpec(
 
717
                    self.user_spec, branch, 'invalid date')
708
718
 
709
719
            dt = datetime.datetime(year=year, month=month, day=day,
710
 
                    hour=hour, minute=minute, second=second)
 
720
                                   hour=hour, minute=minute, second=second)
711
721
        with branch.lock_read():
712
722
            rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
713
723
        if rev == branch.revno():
714
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
724
            raise InvalidRevisionSpec(self.user_spec, branch)
715
725
        return RevisionInfo(branch, rev)
716
726
 
717
727
 
718
 
 
719
728
class RevisionSpec_ancestor(RevisionSpec):
720
729
    """Selects a common ancestor with a second branch."""
721
730
 
829
838
        return self.spec
830
839
 
831
840
 
832
 
 
833
841
class RevisionSpec_submit(RevisionSpec_ancestor):
834
842
    """Selects a common ancestor with a submit branch."""
835
843
 
860
868
        if submit_location is None:
861
869
            raise errors.NoSubmitBranch(branch)
862
870
        trace.note(gettext('Using {0} {1}').format(location_type,
863
 
                                                        submit_location))
 
871
                                                   submit_location))
864
872
        return submit_location
865
873
 
866
874
    def _match_on(self, branch, revs):
867
875
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
868
876
        return self._find_revision_info(branch,
869
 
            self._get_submit_location(branch))
 
877
                                        self._get_submit_location(branch))
870
878
 
871
879
    def _as_revision_id(self, context_branch):
872
880
        return self._find_revision_id(context_branch,
873
 
            self._get_submit_location(context_branch))
 
881
                                      self._get_submit_location(context_branch))
874
882
 
875
883
 
876
884
class RevisionSpec_annotate(RevisionIDSpec):
886
894
    """
887
895
 
888
896
    def _raise_invalid(self, numstring, context_branch):
889
 
        raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
897
        raise InvalidRevisionSpec(
 
898
            self.user_spec, context_branch,
890
899
            'No such line: %s' % numstring)
891
900
 
892
901
    def _as_revision_id(self, context_branch):
898
907
        tree, file_path = workingtree.WorkingTree.open_containing(path)
899
908
        with tree.lock_read():
900
909
            if not tree.has_filename(file_path):
901
 
                raise errors.InvalidRevisionSpec(self.user_spec,
902
 
                    context_branch, "File '%s' is not versioned." %
903
 
                    file_path)
 
910
                raise InvalidRevisionSpec(
 
911
                    self.user_spec, context_branch,
 
912
                    "File '%s' is not versioned." % file_path)
904
913
            revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
905
914
        try:
906
915
            revision_id = revision_ids[index]
907
916
        except IndexError:
908
917
            self._raise_invalid(numstring, context_branch)
909
918
        if revision_id == revision.CURRENT_REVISION:
910
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
919
            raise InvalidRevisionSpec(
 
920
                self.user_spec, context_branch,
911
921
                'Line %s has not been committed.' % numstring)
912
922
        return revision_id
913
923
 
932
942
        result = graph.find_lefthand_merger(revision_id,
933
943
                                            context_branch.last_revision())
934
944
        if result is None:
935
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
 
945
            raise InvalidRevisionSpec(self.user_spec, context_branch)
936
946
        return result
937
947
 
938
948
 
945
955
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
946
956
 
947
957
revspec_registry = registry.Registry()
 
958
 
 
959
 
948
960
def _register_revspec(revspec):
949
961
    revspec_registry.register(revspec.prefix, revspec)
950
962
 
 
963
 
951
964
_register_revspec(RevisionSpec_revno)
952
965
_register_revspec(RevisionSpec_revid)
953
966
_register_revspec(RevisionSpec_last)