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

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
 
18
import re
 
19
 
18
20
from bzrlib.lazy_import import lazy_import
19
21
lazy_import(globals(), """
20
22
import bisect
21
23
import datetime
 
24
""")
22
25
 
23
26
from bzrlib import (
24
 
    branch as _mod_branch,
 
27
    errors,
25
28
    osutils,
 
29
    registry,
26
30
    revision,
27
31
    symbol_versioning,
28
 
    workingtree,
29
 
    )
30
 
from bzrlib.i18n import gettext
31
 
""")
32
 
 
33
 
from bzrlib import (
34
 
    errors,
35
 
    lazy_regex,
36
 
    registry,
37
32
    trace,
38
33
    )
39
34
 
118
113
        return RevisionInfo(branch, revno, revision_id)
119
114
 
120
115
 
 
116
_revno_regex = None
 
117
 
 
118
 
121
119
class RevisionSpec(object):
122
120
    """A parsed revision specification."""
123
121
 
168
166
                         spectype.__name__, spec)
169
167
            return spectype(spec, _internal=True)
170
168
        else:
 
169
            for spectype in SPEC_TYPES:
 
170
                if spec.startswith(spectype.prefix):
 
171
                    trace.mutter('Returning RevisionSpec %s for %s',
 
172
                                 spectype.__name__, spec)
 
173
                    return spectype(spec, _internal=True)
171
174
            # Otherwise treat it as a DWIM, build the RevisionSpec object and
172
175
            # wait for _match_on to be called.
173
176
            return RevisionSpec_dwim(spec, _internal=True)
209
212
    def in_history(self, branch):
210
213
        if branch:
211
214
            if self.wants_revision_history:
212
 
                # TODO: avoid looking at all of history
213
 
                branch.lock_read()
214
 
                try:
215
 
                    graph = branch.repository.get_graph()
216
 
                    revs = list(graph.iter_lefthand_ancestry(
217
 
                        branch.last_revision(), [revision.NULL_REVISION]))
218
 
                finally:
219
 
                    branch.unlock()
220
 
                revs.reverse()
 
215
                revs = branch.revision_history()
221
216
            else:
222
217
                revs = None
223
218
        else:
307
302
    # each revspec we try.
308
303
    wants_revision_history = False
309
304
 
310
 
    _revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
311
 
 
312
 
    # The revspecs to try
313
 
    _possible_revspecs = []
314
 
 
315
305
    def _try_spectype(self, rstype, branch):
316
306
        rs = rstype(self.spec, _internal=True)
317
307
        # Hit in_history to find out if it exists, or we need to try the
322
312
        """Run the lookup and see what we can get."""
323
313
 
324
314
        # First, see if it's a revno
325
 
        if self._revno_regex.match(self.spec) is not None:
 
315
        global _revno_regex
 
316
        if _revno_regex is None:
 
317
            _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
 
318
        if _revno_regex.match(self.spec) is not None:
326
319
            try:
327
320
                return self._try_spectype(RevisionSpec_revno, branch)
328
321
            except RevisionSpec_revno.dwim_catchable_exceptions:
329
322
                pass
330
323
 
331
324
        # Next see what has been registered
332
 
        for objgetter in self._possible_revspecs:
333
 
            rs_class = objgetter.get_obj()
334
 
            try:
335
 
                return self._try_spectype(rs_class, branch)
336
 
            except rs_class.dwim_catchable_exceptions:
337
 
                pass
338
 
 
339
 
        # Try the old (deprecated) dwim list:
340
325
        for rs_class in dwim_revspecs:
341
326
            try:
342
327
                return self._try_spectype(rs_class, branch)
348
333
        # really relevant.
349
334
        raise errors.InvalidRevisionSpec(self.spec, branch)
350
335
 
351
 
    @classmethod
352
 
    def append_possible_revspec(cls, revspec):
353
 
        """Append a possible DWIM revspec.
354
 
 
355
 
        :param revspec: Revision spec to try.
356
 
        """
357
 
        cls._possible_revspecs.append(registry._ObjectGetter(revspec))
358
 
 
359
 
    @classmethod
360
 
    def append_possible_lazy_revspec(cls, module_name, member_name):
361
 
        """Append a possible lazily loaded DWIM revspec.
362
 
 
363
 
        :param module_name: Name of the module with the revspec
364
 
        :param member_name: Name of the revspec within the module
365
 
        """
366
 
        cls._possible_revspecs.append(
367
 
            registry._LazyObjectGetter(module_name, member_name))
368
 
 
369
336
 
370
337
class RevisionSpec_revno(RevisionSpec):
371
338
    """Selects a revision using a number."""
477
444
 
478
445
 
479
446
 
480
 
class RevisionIDSpec(RevisionSpec):
481
 
 
482
 
    def _match_on(self, branch, revs):
483
 
        revision_id = self.as_revision_id(branch)
484
 
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
485
 
 
486
 
 
487
 
class RevisionSpec_revid(RevisionIDSpec):
 
447
class RevisionSpec_revid(RevisionSpec):
488
448
    """Selects a revision using the revision id."""
489
449
 
490
450
    help_txt = """Selects a revision using the revision id.
499
459
 
500
460
    prefix = 'revid:'
501
461
 
502
 
    def _as_revision_id(self, context_branch):
 
462
    def _match_on(self, branch, revs):
503
463
        # self.spec comes straight from parsing the command line arguments,
504
464
        # so we expect it to be a Unicode string. Switch it to the internal
505
465
        # representation.
 
466
        revision_id = osutils.safe_revision_id(self.spec, warn=False)
 
467
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
 
468
 
 
469
    def _as_revision_id(self, context_branch):
506
470
        return osutils.safe_revision_id(self.spec, warn=False)
507
471
 
508
472
 
694
658
                                   August 14th, 2006 at 5:10pm.
695
659
    """
696
660
    prefix = 'date:'
697
 
    _date_regex = lazy_regex.lazy_compile(
 
661
    _date_re = re.compile(
698
662
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
699
663
            r'(,|T)?\s*'
700
664
            r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
718
682
        elif self.spec.lower() == 'tomorrow':
719
683
            dt = today + datetime.timedelta(days=1)
720
684
        else:
721
 
            m = self._date_regex.match(self.spec)
 
685
            m = self._date_re.match(self.spec)
722
686
            if not m or (not m.group('date') and not m.group('time')):
723
687
                raise errors.InvalidRevisionSpec(self.user_spec,
724
688
                                                 branch, 'invalid date')
849
813
        revision_b = other_branch.last_revision()
850
814
        if revision_b in (None, revision.NULL_REVISION):
851
815
            raise errors.NoCommits(other_branch)
852
 
        if branch is None:
853
 
            branch = other_branch
854
 
        else:
855
 
            try:
856
 
                # pull in the remote revisions so we can diff
857
 
                branch.fetch(other_branch, revision_b)
858
 
            except errors.ReadOnlyError:
859
 
                branch = other_branch
 
816
        # pull in the remote revisions so we can diff
 
817
        branch.fetch(other_branch, revision_b)
860
818
        try:
861
819
            revno = branch.revision_id_to_revno(revision_b)
862
820
        except errors.NoSuchRevision:
882
840
            raise errors.NoCommits(other_branch)
883
841
        return other_branch.repository.revision_tree(last_revision)
884
842
 
885
 
    def needs_branch(self):
886
 
        return False
887
 
 
888
 
    def get_branch(self):
889
 
        return self.spec
890
 
 
891
843
 
892
844
 
893
845
class RevisionSpec_submit(RevisionSpec_ancestor):
919
871
            location_type = 'parent branch'
920
872
        if submit_location is None:
921
873
            raise errors.NoSubmitBranch(branch)
922
 
        trace.note(gettext('Using {0} {1}').format(location_type,
923
 
                                                        submit_location))
 
874
        trace.note('Using %s %s', location_type, submit_location)
924
875
        return submit_location
925
876
 
926
877
    def _match_on(self, branch, revs):
933
884
            self._get_submit_location(context_branch))
934
885
 
935
886
 
936
 
class RevisionSpec_annotate(RevisionIDSpec):
937
 
 
938
 
    prefix = 'annotate:'
939
 
 
940
 
    help_txt = """Select the revision that last modified the specified line.
941
 
 
942
 
    Select the revision that last modified the specified line.  Line is
943
 
    specified as path:number.  Path is a relative path to the file.  Numbers
944
 
    start at 1, and are relative to the current version, not the last-
945
 
    committed version of the file.
946
 
    """
947
 
 
948
 
    def _raise_invalid(self, numstring, context_branch):
949
 
        raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
950
 
            'No such line: %s' % numstring)
951
 
 
952
 
    def _as_revision_id(self, context_branch):
953
 
        path, numstring = self.spec.rsplit(':', 1)
954
 
        try:
955
 
            index = int(numstring) - 1
956
 
        except ValueError:
957
 
            self._raise_invalid(numstring, context_branch)
958
 
        tree, file_path = workingtree.WorkingTree.open_containing(path)
959
 
        tree.lock_read()
960
 
        try:
961
 
            file_id = tree.path2id(file_path)
962
 
            if file_id is None:
963
 
                raise errors.InvalidRevisionSpec(self.user_spec,
964
 
                    context_branch, "File '%s' is not versioned." %
965
 
                    file_path)
966
 
            revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
967
 
        finally:
968
 
            tree.unlock()
969
 
        try:
970
 
            revision_id = revision_ids[index]
971
 
        except IndexError:
972
 
            self._raise_invalid(numstring, context_branch)
973
 
        if revision_id == revision.CURRENT_REVISION:
974
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
975
 
                'Line %s has not been committed.' % numstring)
976
 
        return revision_id
977
 
 
978
 
 
979
 
class RevisionSpec_mainline(RevisionIDSpec):
980
 
 
981
 
    help_txt = """Select mainline revision that merged the specified revision.
982
 
 
983
 
    Select the revision that merged the specified revision into mainline.
984
 
    """
985
 
 
986
 
    prefix = 'mainline:'
987
 
 
988
 
    def _as_revision_id(self, context_branch):
989
 
        revspec = RevisionSpec.from_string(self.spec)
990
 
        if revspec.get_branch() is None:
991
 
            spec_branch = context_branch
992
 
        else:
993
 
            spec_branch = _mod_branch.Branch.open(revspec.get_branch())
994
 
        revision_id = revspec.as_revision_id(spec_branch)
995
 
        graph = context_branch.repository.get_graph()
996
 
        result = graph.find_lefthand_merger(revision_id,
997
 
                                            context_branch.last_revision())
998
 
        if result is None:
999
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
1000
 
        return result
1001
 
 
1002
 
 
1003
887
# The order in which we want to DWIM a revision spec without any prefix.
1004
888
# revno is always tried first and isn't listed here, this is used by
1005
889
# RevisionSpec_dwim._match_on
1006
 
dwim_revspecs = symbol_versioning.deprecated_list(
1007
 
    symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
 
890
dwim_revspecs = [
 
891
    RevisionSpec_tag, # Let's try for a tag
 
892
    RevisionSpec_revid, # Maybe it's a revid?
 
893
    RevisionSpec_date, # Perhaps a date?
 
894
    RevisionSpec_branch, # OK, last try, maybe it's a branch
 
895
    ]
1008
896
 
1009
 
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
1010
 
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
1011
 
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
1012
 
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
1013
897
 
1014
898
revspec_registry = registry.Registry()
1015
899
def _register_revspec(revspec):
1024
908
_register_revspec(RevisionSpec_ancestor)
1025
909
_register_revspec(RevisionSpec_branch)
1026
910
_register_revspec(RevisionSpec_submit)
1027
 
_register_revspec(RevisionSpec_annotate)
1028
 
_register_revspec(RevisionSpec_mainline)
 
911
 
 
912
# classes in this list should have a "prefix" attribute, against which
 
913
# string specs are matched
 
914
SPEC_TYPES = symbol_versioning.deprecated_list(
 
915
    symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])