~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: John Arbash Meinel
  • Author(s): Mark Hammond
  • Date: 2008-09-09 17:02:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: john@arbash-meinel.com-20080909170221-svim3jw2mrz0amp3
An updated transparent icon for bzr.

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., 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(), """
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
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)
 
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)
162
169
        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)
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
185
187
            called directly. Only from RevisionSpec.from_string()
186
188
        """
187
189
        if not _internal:
 
190
            # XXX: Update this after 0.10 is released
188
191
            symbol_versioning.warn('Creating a RevisionSpec directly has'
189
192
                                   ' been deprecated in version 0.11. Use'
190
193
                                   ' RevisionSpec.from_string()'
249
252
        """
250
253
        return self.in_history(context_branch).rev_id
251
254
 
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
 
 
271
255
    def __repr__(self):
272
256
        # this is mostly for helping with testing
273
257
        return '<%s %s>' % (self.__class__.__name__,
274
258
                              self.user_spec)
275
 
 
 
259
    
276
260
    def needs_branch(self):
277
261
        """Whether this revision spec needs a branch.
278
262
 
282
266
 
283
267
    def get_branch(self):
284
268
        """When the revision specifier contains a branch location, return it.
285
 
 
 
269
        
286
270
        Otherwise, return None.
287
271
        """
288
272
        return None
302
286
    than the branch's history, the first revision is returned.
303
287
    Examples::
304
288
 
305
 
      revno:1                   -> return the first revision of this branch
 
289
      revno:1                   -> return the first revision
306
290
      revno:3:/path/to/branch   -> return the 3rd revision of
307
291
                                   the branch '/path/to/branch'
308
292
      revno:-1                  -> The last revision in a branch.
339
323
                dotted = False
340
324
            except ValueError:
341
325
                # dotted decimal. This arguably should not be here
342
 
                # but the from_string method is a little primitive
 
326
                # but the from_string method is a little primitive 
343
327
                # right now - RBC 20060928
344
328
                try:
345
329
                    match_revno = tuple((int(number) for number in revno_spec.split('.')))
357
341
            revs_or_none = None
358
342
 
359
343
        if dotted:
 
344
            branch.lock_read()
360
345
            try:
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)
 
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
365
354
            else:
366
355
                # there is no traditional 'revno' for dotted-decimal revnos.
367
356
                # so for  API compatability we return None.
368
 
                return branch, None, revision_id
 
357
                return branch, None, revisions[0]
369
358
        else:
370
359
            last_revno, last_revision_id = branch.last_revision_info()
371
360
            if revno < 0:
395
384
        else:
396
385
            return self.spec[self.spec.find(':')+1:]
397
386
 
398
 
# Old compatibility
 
387
# Old compatibility 
399
388
RevisionSpec_int = RevisionSpec_revno
400
389
 
 
390
SPEC_TYPES.append(RevisionSpec_revno)
401
391
 
402
392
 
403
393
class RevisionSpec_revid(RevisionSpec):
406
396
    help_txt = """Selects a revision using the revision id.
407
397
 
408
398
    Supply a specific revision id, that can be used to specify any
409
 
    revision id in the ancestry of the branch.
 
399
    revision id in the ancestry of the branch. 
410
400
    Including merges, and pending merges.
411
401
    Examples::
412
402
 
425
415
    def _as_revision_id(self, context_branch):
426
416
        return osutils.safe_revision_id(self.spec, warn=False)
427
417
 
 
418
SPEC_TYPES.append(RevisionSpec_revid)
428
419
 
429
420
 
430
421
class RevisionSpec_last(RevisionSpec):
476
467
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
477
468
        return revision_id
478
469
 
 
470
SPEC_TYPES.append(RevisionSpec_last)
479
471
 
480
472
 
481
473
class RevisionSpec_before(RevisionSpec):
483
475
 
484
476
    help_txt = """Selects the parent of the revision specified.
485
477
 
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
 
 
 
478
    Supply any revision spec to return the parent of that revision.
490
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.
491
482
 
492
483
    Examples::
493
484
 
494
485
      before:1913    -> Return the parent of revno 1913 (revno 1912)
495
486
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
496
487
                                            aaaa@bbbb-1234567890
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
 
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)
501
491
    """
502
492
 
503
493
    prefix = 'before:'
504
 
 
 
494
    
505
495
    def _match_on(self, branch, revs):
506
496
        r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
507
497
        if r.revno == 0:
550
540
                'No parents for revision.')
551
541
        return parents[0]
552
542
 
 
543
SPEC_TYPES.append(RevisionSpec_before)
553
544
 
554
545
 
555
546
class RevisionSpec_tag(RevisionSpec):
571
562
    def _as_revision_id(self, context_branch):
572
563
        return context_branch.tags.lookup_tag(self.spec)
573
564
 
 
565
SPEC_TYPES.append(RevisionSpec_tag)
574
566
 
575
567
 
576
568
class _RevListToTimestamps(object):
611
603
      date:yesterday            -> select the first revision since yesterday
612
604
      date:2006-08-14,17:10:14  -> select the first revision after
613
605
                                   August 14th, 2006 at 5:10pm.
614
 
    """
 
606
    """    
615
607
    prefix = 'date:'
616
608
    _date_re = re.compile(
617
609
            r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
677
669
        else:
678
670
            return RevisionInfo(branch, rev + 1)
679
671
 
 
672
SPEC_TYPES.append(RevisionSpec_date)
680
673
 
681
674
 
682
675
class RevisionSpec_ancestor(RevisionSpec):
727
720
            revision_a = revision.ensure_null(branch.last_revision())
728
721
            if revision_a == revision.NULL_REVISION:
729
722
                raise errors.NoCommits(branch)
730
 
            if other_location == '':
731
 
                other_location = branch.get_parent()
732
723
            other_branch = Branch.open(other_location)
733
724
            other_branch.lock_read()
734
725
            try:
746
737
            branch.unlock()
747
738
 
748
739
 
 
740
SPEC_TYPES.append(RevisionSpec_ancestor)
749
741
 
750
742
 
751
743
class RevisionSpec_branch(RevisionSpec):
785
777
            raise errors.NoCommits(other_branch)
786
778
        return last_revision
787
779
 
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
 
 
 
780
SPEC_TYPES.append(RevisionSpec_branch)
797
781
 
798
782
 
799
783
class RevisionSpec_submit(RevisionSpec_ancestor):
838
822
            self._get_submit_location(context_branch))
839
823
 
840
824
 
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", [])
 
825
SPEC_TYPES.append(RevisionSpec_submit)