~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711214124-qi09irlj7pd5cuzg
Shortcut the case when one revision is in the ancestry of the other.

At the cost of a heads() check, when one parent supersedes, we don't have to extract
the text for the other. Changes merge time from 3m37s => 3m21s. Using a
CachingParentsProvider would drop the time down to 3m11s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
import re
19
 
 
20
 
from bzrlib.lazy_import import lazy_import
21
 
lazy_import(globals(), """
22
18
import bisect
23
19
import datetime
24
 
""")
 
20
import re
25
21
 
26
22
from bzrlib import (
27
23
    errors,
28
24
    osutils,
29
 
    registry,
30
25
    revision,
31
26
    symbol_versioning,
32
27
    trace,
 
28
    tsort,
33
29
    )
34
30
 
35
31
 
115
111
 
116
112
# classes in this list should have a "prefix" attribute, against which
117
113
# string specs are matched
 
114
SPEC_TYPES = []
118
115
_revno_regex = None
119
116
 
120
117
 
140
137
    prefix = None
141
138
    wants_revision_history = True
142
139
 
 
140
    def __new__(cls, spec, _internal=False):
 
141
        if _internal:
 
142
            return object.__new__(cls, spec, _internal=_internal)
 
143
 
 
144
        symbol_versioning.warn('Creating a RevisionSpec directly has'
 
145
                               ' been deprecated in version 0.11. Use'
 
146
                               ' RevisionSpec.from_string()'
 
147
                               ' instead.',
 
148
                               DeprecationWarning, stacklevel=2)
 
149
        return RevisionSpec.from_string(spec)
 
150
 
143
151
    @staticmethod
144
152
    def from_string(spec):
145
153
        """Parse a revision spec string into a RevisionSpec object.
153
161
 
154
162
        if spec is None:
155
163
            return RevisionSpec(None, _internal=True)
156
 
        match = revspec_registry.get_prefix(spec)
157
 
        if match is not None:
158
 
            spectype, specsuffix = match
159
 
            trace.mutter('Returning RevisionSpec %s for %s',
160
 
                         spectype.__name__, spec)
161
 
            return spectype(spec, _internal=True)
162
 
        else:
163
 
            for spectype in SPEC_TYPES:
 
164
        for spectype in SPEC_TYPES:
 
165
            if spec.startswith(spectype.prefix):
164
166
                trace.mutter('Returning RevisionSpec %s for %s',
165
167
                             spectype.__name__, spec)
166
 
                if spec.startswith(spectype.prefix):
167
 
                    return spectype(spec, _internal=True)
 
168
                return spectype(spec, _internal=True)
 
169
        else:
168
170
            # RevisionSpec_revno is special cased, because it is the only
169
171
            # one that directly handles plain integers
170
172
            # TODO: This should not be special cased rather it should be
250
252
        """
251
253
        return self.in_history(context_branch).rev_id
252
254
 
253
 
    def as_tree(self, context_branch):
254
 
        """Return the tree object for this revisions spec.
255
 
 
256
 
        Some revision specs require a context_branch to be able to determine
257
 
        the revision id and access the repository. Not all specs will make
258
 
        use of it.
259
 
        """
260
 
        return self._as_tree(context_branch)
261
 
 
262
 
    def _as_tree(self, context_branch):
263
 
        """Implementation of as_tree().
264
 
 
265
 
        Classes should override this function to provide appropriate
266
 
        functionality. The default is to just call '.as_revision_id()'
267
 
        and get the revision tree from context_branch's repository.
268
 
        """
269
 
        revision_id = self.as_revision_id(context_branch)
270
 
        return context_branch.repository.revision_tree(revision_id)
271
 
 
272
255
    def __repr__(self):
273
256
        # this is mostly for helping with testing
274
257
        return '<%s %s>' % (self.__class__.__name__,
303
286
    than the branch's history, the first revision is returned.
304
287
    Examples::
305
288
 
306
 
      revno:1                   -> return the first revision of this branch
 
289
      revno:1                   -> return the first revision
307
290
      revno:3:/path/to/branch   -> return the 3rd revision of
308
291
                                   the branch '/path/to/branch'
309
292
      revno:-1                  -> The last revision in a branch.
358
341
            revs_or_none = None
359
342
 
360
343
        if dotted:
 
344
            branch.lock_read()
361
345
            try:
362
 
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
363
 
                    _cache_reverse=True)
364
 
            except errors.NoSuchRevision:
365
 
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
346
                revision_id_to_revno = branch.get_revision_id_to_revno_map()
 
347
                revisions = [revision_id for revision_id, revno
 
348
                             in revision_id_to_revno.iteritems()
 
349
                             if revno == match_revno]
 
350
            finally:
 
351
                branch.unlock()
 
352
            if len(revisions) != 1:
 
353
                return branch, None, None
366
354
            else:
367
355
                # there is no traditional 'revno' for dotted-decimal revnos.
368
356
                # so for  API compatability we return None.
369
 
                return branch, None, revision_id
 
357
                return branch, None, revisions[0]
370
358
        else:
371
359
            last_revno, last_revision_id = branch.last_revision_info()
372
360
            if revno < 0:
399
387
# Old compatibility 
400
388
RevisionSpec_int = RevisionSpec_revno
401
389
 
 
390
SPEC_TYPES.append(RevisionSpec_revno)
402
391
 
403
392
 
404
393
class RevisionSpec_revid(RevisionSpec):
426
415
    def _as_revision_id(self, context_branch):
427
416
        return osutils.safe_revision_id(self.spec, warn=False)
428
417
 
 
418
SPEC_TYPES.append(RevisionSpec_revid)
429
419
 
430
420
 
431
421
class RevisionSpec_last(RevisionSpec):
477
467
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
478
468
        return revision_id
479
469
 
 
470
SPEC_TYPES.append(RevisionSpec_last)
480
471
 
481
472
 
482
473
class RevisionSpec_before(RevisionSpec):
484
475
 
485
476
    help_txt = """Selects the parent of the revision specified.
486
477
 
487
 
    Supply any revision spec to return the parent of that revision.  This is
488
 
    mostly useful when inspecting revisions that are not in the revision history
489
 
    of a branch.
490
 
 
 
478
    Supply any revision spec to return the parent of that revision.
491
479
    It is an error to request the parent of the null revision (before:0).
 
480
    This is mostly useful when inspecting revisions that are not in the
 
481
    revision history of a branch.
492
482
 
493
483
    Examples::
494
484
 
495
485
      before:1913    -> Return the parent of revno 1913 (revno 1912)
496
486
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
497
487
                                            aaaa@bbbb-1234567890
498
 
      bzr diff -r before:1913..1913
499
 
            -> Find the changes between revision 1913 and its parent (1912).
500
 
               (What changes did revision 1913 introduce).
501
 
               This is equivalent to:  bzr diff -c 1913
 
488
      bzr diff -r before:revid:aaaa..revid:aaaa
 
489
            -> Find the changes between revision 'aaaa' and its parent.
 
490
               (what changes did 'aaaa' introduce)
502
491
    """
503
492
 
504
493
    prefix = 'before:'
551
540
                'No parents for revision.')
552
541
        return parents[0]
553
542
 
 
543
SPEC_TYPES.append(RevisionSpec_before)
554
544
 
555
545
 
556
546
class RevisionSpec_tag(RevisionSpec):
572
562
    def _as_revision_id(self, context_branch):
573
563
        return context_branch.tags.lookup_tag(self.spec)
574
564
 
 
565
SPEC_TYPES.append(RevisionSpec_tag)
575
566
 
576
567
 
577
568
class _RevListToTimestamps(object):
605
596
 
606
597
    One way to display all the changes since yesterday would be::
607
598
 
608
 
        bzr log -r date:yesterday..
 
599
        bzr log -r date:yesterday..-1
609
600
 
610
601
    Examples::
611
602
 
678
669
        else:
679
670
            return RevisionInfo(branch, rev + 1)
680
671
 
 
672
SPEC_TYPES.append(RevisionSpec_date)
681
673
 
682
674
 
683
675
class RevisionSpec_ancestor(RevisionSpec):
728
720
            revision_a = revision.ensure_null(branch.last_revision())
729
721
            if revision_a == revision.NULL_REVISION:
730
722
                raise errors.NoCommits(branch)
731
 
            if other_location == '':
732
 
                other_location = branch.get_parent()
733
723
            other_branch = Branch.open(other_location)
734
724
            other_branch.lock_read()
735
725
            try:
747
737
            branch.unlock()
748
738
 
749
739
 
 
740
SPEC_TYPES.append(RevisionSpec_ancestor)
750
741
 
751
742
 
752
743
class RevisionSpec_branch(RevisionSpec):
786
777
            raise errors.NoCommits(other_branch)
787
778
        return last_revision
788
779
 
789
 
    def _as_tree(self, context_branch):
790
 
        from bzrlib.branch import Branch
791
 
        other_branch = Branch.open(self.spec)
792
 
        last_revision = other_branch.last_revision()
793
 
        last_revision = revision.ensure_null(last_revision)
794
 
        if last_revision == revision.NULL_REVISION:
795
 
            raise errors.NoCommits(other_branch)
796
 
        return other_branch.repository.revision_tree(last_revision)
797
 
 
 
780
SPEC_TYPES.append(RevisionSpec_branch)
798
781
 
799
782
 
800
783
class RevisionSpec_submit(RevisionSpec_ancestor):
804
787
 
805
788
    Diffing against this shows all the changes that were made in this branch,
806
789
    and is a good predictor of what merge will do.  The submit branch is
807
 
    used by the bundle and merge directive commands.  If no submit branch
 
790
    used by the bundle and merge directive comands.  If no submit branch
808
791
    is specified, the parent branch is used instead.
809
792
 
810
793
    The common ancestor is the last revision that existed in both
839
822
            self._get_submit_location(context_branch))
840
823
 
841
824
 
842
 
revspec_registry = registry.Registry()
843
 
def _register_revspec(revspec):
844
 
    revspec_registry.register(revspec.prefix, revspec)
845
 
 
846
 
_register_revspec(RevisionSpec_revno)
847
 
_register_revspec(RevisionSpec_revid)
848
 
_register_revspec(RevisionSpec_last)
849
 
_register_revspec(RevisionSpec_before)
850
 
_register_revspec(RevisionSpec_tag)
851
 
_register_revspec(RevisionSpec_date)
852
 
_register_revspec(RevisionSpec_ancestor)
853
 
_register_revspec(RevisionSpec_branch)
854
 
_register_revspec(RevisionSpec_submit)
855
 
 
856
 
SPEC_TYPES = symbol_versioning.deprecated_list(
857
 
    symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])
 
825
SPEC_TYPES.append(RevisionSpec_submit)