~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
 
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
import re
 
19
 
 
20
from bzrlib.lazy_import import lazy_import
 
21
lazy_import(globals(), """
18
22
import bisect
19
23
import datetime
20
 
import re
 
24
""")
21
25
 
22
26
from bzrlib import (
23
27
    errors,
24
28
    osutils,
 
29
    registry,
25
30
    revision,
26
31
    symbol_versioning,
27
32
    trace,
28
 
    tsort,
29
33
    )
30
34
 
31
35
 
111
115
 
112
116
# classes in this list should have a "prefix" attribute, against which
113
117
# string specs are matched
114
 
SPEC_TYPES = []
115
118
_revno_regex = None
116
119
 
117
120
 
137
140
    prefix = None
138
141
    wants_revision_history = True
139
142
 
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
 
 
151
143
    @staticmethod
152
144
    def from_string(spec):
153
145
        """Parse a revision spec string into a RevisionSpec object.
161
153
 
162
154
        if spec is None:
163
155
            return RevisionSpec(None, _internal=True)
164
 
        for spectype in SPEC_TYPES:
165
 
            if spec.startswith(spectype.prefix):
166
 
                trace.mutter('Returning RevisionSpec %s for %s',
167
 
                             spectype.__name__, spec)
168
 
                return spectype(spec, _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)
169
162
        else:
 
163
            for spectype in SPEC_TYPES:
 
164
                if spec.startswith(spectype.prefix):
 
165
                    trace.mutter('Returning RevisionSpec %s for %s',
 
166
                                 spectype.__name__, spec)
 
167
                    return spectype(spec, _internal=True)
170
168
            # RevisionSpec_revno is special cased, because it is the only
171
169
            # one that directly handles plain integers
172
170
            # TODO: This should not be special cased rather it should be
187
185
            called directly. Only from RevisionSpec.from_string()
188
186
        """
189
187
        if not _internal:
190
 
            # XXX: Update this after 0.10 is released
191
188
            symbol_versioning.warn('Creating a RevisionSpec directly has'
192
189
                                   ' been deprecated in version 0.11. Use'
193
190
                                   ' RevisionSpec.from_string()'
252
249
        """
253
250
        return self.in_history(context_branch).rev_id
254
251
 
 
252
    def as_tree(self, context_branch):
 
253
        """Return the tree object for this revisions spec.
 
254
 
 
255
        Some revision specs require a context_branch to be able to determine
 
256
        the revision id and access the repository. Not all specs will make
 
257
        use of it.
 
258
        """
 
259
        return self._as_tree(context_branch)
 
260
 
 
261
    def _as_tree(self, context_branch):
 
262
        """Implementation of as_tree().
 
263
 
 
264
        Classes should override this function to provide appropriate
 
265
        functionality. The default is to just call '.as_revision_id()'
 
266
        and get the revision tree from context_branch's repository.
 
267
        """
 
268
        revision_id = self.as_revision_id(context_branch)
 
269
        return context_branch.repository.revision_tree(revision_id)
 
270
 
255
271
    def __repr__(self):
256
272
        # this is mostly for helping with testing
257
273
        return '<%s %s>' % (self.__class__.__name__,
258
274
                              self.user_spec)
259
 
    
 
275
 
260
276
    def needs_branch(self):
261
277
        """Whether this revision spec needs a branch.
262
278
 
266
282
 
267
283
    def get_branch(self):
268
284
        """When the revision specifier contains a branch location, return it.
269
 
        
 
285
 
270
286
        Otherwise, return None.
271
287
        """
272
288
        return None
286
302
    than the branch's history, the first revision is returned.
287
303
    Examples::
288
304
 
289
 
      revno:1                   -> return the first revision
 
305
      revno:1                   -> return the first revision of this branch
290
306
      revno:3:/path/to/branch   -> return the 3rd revision of
291
307
                                   the branch '/path/to/branch'
292
308
      revno:-1                  -> The last revision in a branch.
323
339
                dotted = False
324
340
            except ValueError:
325
341
                # dotted decimal. This arguably should not be here
326
 
                # but the from_string method is a little primitive 
 
342
                # but the from_string method is a little primitive
327
343
                # right now - RBC 20060928
328
344
                try:
329
345
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
341
357
            revs_or_none = None
342
358
 
343
359
        if dotted:
344
 
            branch.lock_read()
345
360
            try:
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
 
361
                revision_id = branch.dotted_revno_to_revision_id(match_revno,
 
362
                    _cache_reverse=True)
 
363
            except errors.NoSuchRevision:
 
364
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
354
365
            else:
355
366
                # there is no traditional 'revno' for dotted-decimal revnos.
356
367
                # so for  API compatability we return None.
357
 
                return branch, None, revisions[0]
 
368
                return branch, None, revision_id
358
369
        else:
359
370
            last_revno, last_revision_id = branch.last_revision_info()
360
371
            if revno < 0:
384
395
        else:
385
396
            return self.spec[self.spec.find(':')+1:]
386
397
 
387
 
# Old compatibility 
 
398
# Old compatibility
388
399
RevisionSpec_int = RevisionSpec_revno
389
400
 
390
 
SPEC_TYPES.append(RevisionSpec_revno)
391
401
 
392
402
 
393
403
class RevisionSpec_revid(RevisionSpec):
396
406
    help_txt = """Selects a revision using the revision id.
397
407
 
398
408
    Supply a specific revision id, that can be used to specify any
399
 
    revision id in the ancestry of the branch. 
 
409
    revision id in the ancestry of the branch.
400
410
    Including merges, and pending merges.
401
411
    Examples::
402
412
 
415
425
    def _as_revision_id(self, context_branch):
416
426
        return osutils.safe_revision_id(self.spec, warn=False)
417
427
 
418
 
SPEC_TYPES.append(RevisionSpec_revid)
419
428
 
420
429
 
421
430
class RevisionSpec_last(RevisionSpec):
467
476
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
468
477
        return revision_id
469
478
 
470
 
SPEC_TYPES.append(RevisionSpec_last)
471
479
 
472
480
 
473
481
class RevisionSpec_before(RevisionSpec):
475
483
 
476
484
    help_txt = """Selects the parent of the revision specified.
477
485
 
478
 
    Supply any revision spec to return the parent of that revision.
 
486
    Supply any revision spec to return the parent of that revision.  This is
 
487
    mostly useful when inspecting revisions that are not in the revision history
 
488
    of a branch.
 
489
 
479
490
    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.
482
491
 
483
492
    Examples::
484
493
 
485
494
      before:1913    -> Return the parent of revno 1913 (revno 1912)
486
495
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
487
496
                                            aaaa@bbbb-1234567890
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)
 
497
      bzr diff -r before:1913..1913
 
498
            -> Find the changes between revision 1913 and its parent (1912).
 
499
               (What changes did revision 1913 introduce).
 
500
               This is equivalent to:  bzr diff -c 1913
491
501
    """
492
502
 
493
503
    prefix = 'before:'
494
 
    
 
504
 
495
505
    def _match_on(self, branch, revs):
496
506
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
497
507
        if r.revno == 0:
540
550
                'No parents for revision.')
541
551
        return parents[0]
542
552
 
543
 
SPEC_TYPES.append(RevisionSpec_before)
544
553
 
545
554
 
546
555
class RevisionSpec_tag(RevisionSpec):
562
571
    def _as_revision_id(self, context_branch):
563
572
        return context_branch.tags.lookup_tag(self.spec)
564
573
 
565
 
SPEC_TYPES.append(RevisionSpec_tag)
566
574
 
567
575
 
568
576
class _RevListToTimestamps(object):
596
604
 
597
605
    One way to display all the changes since yesterday would be::
598
606
 
599
 
        bzr log -r date:yesterday..-1
 
607
        bzr log -r date:yesterday..
600
608
 
601
609
    Examples::
602
610
 
603
611
      date:yesterday            -> select the first revision since yesterday
604
612
      date:2006-08-14,17:10:14  -> select the first revision after
605
613
                                   August 14th, 2006 at 5:10pm.
606
 
    """    
 
614
    """
607
615
    prefix = 'date:'
608
616
    _date_re = re.compile(
609
617
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
669
677
        else:
670
678
            return RevisionInfo(branch, rev + 1)
671
679
 
672
 
SPEC_TYPES.append(RevisionSpec_date)
673
680
 
674
681
 
675
682
class RevisionSpec_ancestor(RevisionSpec):
720
727
            revision_a = revision.ensure_null(branch.last_revision())
721
728
            if revision_a == revision.NULL_REVISION:
722
729
                raise errors.NoCommits(branch)
 
730
            if other_location == '':
 
731
                other_location = branch.get_parent()
723
732
            other_branch = Branch.open(other_location)
724
733
            other_branch.lock_read()
725
734
            try:
737
746
            branch.unlock()
738
747
 
739
748
 
740
 
SPEC_TYPES.append(RevisionSpec_ancestor)
741
749
 
742
750
 
743
751
class RevisionSpec_branch(RevisionSpec):
777
785
            raise errors.NoCommits(other_branch)
778
786
        return last_revision
779
787
 
780
 
SPEC_TYPES.append(RevisionSpec_branch)
 
788
    def _as_tree(self, context_branch):
 
789
        from bzrlib.branch import Branch
 
790
        other_branch = Branch.open(self.spec)
 
791
        last_revision = other_branch.last_revision()
 
792
        last_revision = revision.ensure_null(last_revision)
 
793
        if last_revision == revision.NULL_REVISION:
 
794
            raise errors.NoCommits(other_branch)
 
795
        return other_branch.repository.revision_tree(last_revision)
 
796
 
781
797
 
782
798
 
783
799
class RevisionSpec_submit(RevisionSpec_ancestor):
787
803
 
788
804
    Diffing against this shows all the changes that were made in this branch,
789
805
    and is a good predictor of what merge will do.  The submit branch is
790
 
    used by the bundle and merge directive comands.  If no submit branch
 
806
    used by the bundle and merge directive commands.  If no submit branch
791
807
    is specified, the parent branch is used instead.
792
808
 
793
809
    The common ancestor is the last revision that existed in both
822
838
            self._get_submit_location(context_branch))
823
839
 
824
840
 
825
 
SPEC_TYPES.append(RevisionSpec_submit)
 
841
revspec_registry = registry.Registry()
 
842
def _register_revspec(revspec):
 
843
    revspec_registry.register(revspec.prefix, revspec)
 
844
 
 
845
_register_revspec(RevisionSpec_revno)
 
846
_register_revspec(RevisionSpec_revid)
 
847
_register_revspec(RevisionSpec_last)
 
848
_register_revspec(RevisionSpec_before)
 
849
_register_revspec(RevisionSpec_tag)
 
850
_register_revspec(RevisionSpec_date)
 
851
_register_revspec(RevisionSpec_ancestor)
 
852
_register_revspec(RevisionSpec_branch)
 
853
_register_revspec(RevisionSpec_submit)
 
854
 
 
855
SPEC_TYPES = symbol_versioning.deprecated_list(
 
856
    symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])