~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: Blake Winton
  • Date: 2007-10-16 16:02:01 UTC
  • mto: This revision was merged to the branch mainline in revision 2921.
  • Revision ID: bwinton@latte.ca-20071016160201-os2bci2ujf7in7an
Change 'print >> f,'s to 'f.write('s.

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,
29
25
    revision,
30
26
    symbol_versioning,
31
27
    trace,
 
28
    tsort,
32
29
    )
33
30
 
34
31
 
103
100
 
104
101
        Use this if you don't know or care what the revno is.
105
102
        """
106
 
        if revision_id == revision.NULL_REVISION:
107
 
            return RevisionInfo(branch, 0, revision_id)
108
103
        try:
109
104
            revno = revs.index(revision_id) + 1
110
105
        except ValueError:
138
133
    """
139
134
 
140
135
    prefix = None
141
 
    wants_revision_history = True
 
136
 
 
137
    def __new__(cls, spec, _internal=False):
 
138
        if _internal:
 
139
            return object.__new__(cls, spec, _internal=_internal)
 
140
 
 
141
        symbol_versioning.warn('Creating a RevisionSpec directly has'
 
142
                               ' been deprecated in version 0.11. Use'
 
143
                               ' RevisionSpec.from_string()'
 
144
                               ' instead.',
 
145
                               DeprecationWarning, stacklevel=2)
 
146
        return RevisionSpec.from_string(spec)
142
147
 
143
148
    @staticmethod
144
149
    def from_string(spec):
153
158
 
154
159
        if spec is None:
155
160
            return RevisionSpec(None, _internal=True)
 
161
 
 
162
        assert isinstance(spec, basestring), \
 
163
            "You should only supply strings not %s" % (type(spec),)
 
164
 
156
165
        for spectype in SPEC_TYPES:
157
166
            if spec.startswith(spectype.prefix):
158
167
                trace.mutter('Returning RevisionSpec %s for %s',
192
201
 
193
202
    def _match_on(self, branch, revs):
194
203
        trace.mutter('Returning RevisionSpec._match_on: None')
195
 
        return RevisionInfo(branch, None, None)
 
204
        return RevisionInfo(branch, 0, None)
196
205
 
197
206
    def _match_on_and_check(self, branch, revs):
198
207
        info = self._match_on(branch, revs)
199
208
        if info:
200
209
            return info
201
 
        elif info == (None, None):
202
 
            # special case - nothing supplied
 
210
        elif info == (0, None):
 
211
            # special case - the empty tree
203
212
            return info
204
213
        elif self.prefix:
205
214
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
208
217
 
209
218
    def in_history(self, branch):
210
219
        if branch:
211
 
            if self.wants_revision_history:
212
 
                revs = branch.revision_history()
213
 
            else:
214
 
                revs = None
 
220
            revs = branch.revision_history()
215
221
        else:
216
222
            # this should never trigger.
217
223
            # TODO: make it a deprecated code path. RBC 20060928
227
233
    # will do what you expect.
228
234
    in_store = in_history
229
235
    in_branch = in_store
230
 
 
231
 
    def as_revision_id(self, context_branch):
232
 
        """Return just the revision_id for this revisions spec.
233
 
 
234
 
        Some revision specs require a context_branch to be able to determine
235
 
        their value. Not all specs will make use of it.
236
 
        """
237
 
        return self._as_revision_id(context_branch)
238
 
 
239
 
    def _as_revision_id(self, context_branch):
240
 
        """Implementation of as_revision_id()
241
 
 
242
 
        Classes should override this function to provide appropriate
243
 
        functionality. The default is to just call '.in_history().rev_id'
244
 
        """
245
 
        return self.in_history(context_branch).rev_id
246
 
 
247
 
    def as_tree(self, context_branch):
248
 
        """Return the tree object for this revisions spec.
249
 
 
250
 
        Some revision specs require a context_branch to be able to determine
251
 
        the revision id and access the repository. Not all specs will make
252
 
        use of it.
253
 
        """
254
 
        return self._as_tree(context_branch)
255
 
 
256
 
    def _as_tree(self, context_branch):
257
 
        """Implementation of as_tree().
258
 
 
259
 
        Classes should override this function to provide appropriate
260
 
        functionality. The default is to just call '.as_revision_id()'
261
 
        and get the revision tree from context_branch's repository.
262
 
        """
263
 
        revision_id = self.as_revision_id(context_branch)
264
 
        return context_branch.repository.revision_tree(revision_id)
265
 
 
 
236
        
266
237
    def __repr__(self):
267
238
        # this is mostly for helping with testing
268
239
        return '<%s %s>' % (self.__class__.__name__,
297
268
    than the branch's history, the first revision is returned.
298
269
    Examples::
299
270
 
300
 
      revno:1                   -> return the first revision of this branch
 
271
      revno:1                   -> return the first revision
301
272
      revno:3:/path/to/branch   -> return the 3rd revision of
302
273
                                   the branch '/path/to/branch'
303
274
      revno:-1                  -> The last revision in a branch.
307
278
                                   your history is very long.
308
279
    """
309
280
    prefix = 'revno:'
310
 
    wants_revision_history = False
311
281
 
312
282
    def _match_on(self, branch, revs):
313
283
        """Lookup a revision by revision number"""
314
 
        branch, revno, revision_id = self._lookup(branch, revs)
315
 
        return RevisionInfo(branch, revno, revision_id)
316
 
 
317
 
    def _lookup(self, branch, revs_or_none):
318
284
        loc = self.spec.find(':')
319
285
        if loc == -1:
320
286
            revno_spec = self.spec
349
315
            # the branch object.
350
316
            from bzrlib.branch import Branch
351
317
            branch = Branch.open(branch_spec)
352
 
            revs_or_none = None
 
318
            # Need to use a new revision history
 
319
            # because we are using a specific branch
 
320
            revs = branch.revision_history()
353
321
 
354
322
        if dotted:
355
323
            branch.lock_read()
361
329
            finally:
362
330
                branch.unlock()
363
331
            if len(revisions) != 1:
364
 
                return branch, None, None
 
332
                return RevisionInfo(branch, None, None)
365
333
            else:
366
334
                # there is no traditional 'revno' for dotted-decimal revnos.
367
335
                # so for  API compatability we return None.
368
 
                return branch, None, revisions[0]
 
336
                return RevisionInfo(branch, None, revisions[0])
369
337
        else:
370
 
            last_revno, last_revision_id = branch.last_revision_info()
371
338
            if revno < 0:
372
339
                # if get_rev_id supported negative revnos, there would not be a
373
340
                # need for this special case.
374
 
                if (-revno) >= last_revno:
 
341
                if (-revno) >= len(revs):
375
342
                    revno = 1
376
343
                else:
377
 
                    revno = last_revno + revno + 1
 
344
                    revno = len(revs) + revno + 1
378
345
            try:
379
 
                revision_id = branch.get_rev_id(revno, revs_or_none)
 
346
                revision_id = branch.get_rev_id(revno, revs)
380
347
            except errors.NoSuchRevision:
381
348
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
382
 
        return branch, revno, revision_id
383
 
 
384
 
    def _as_revision_id(self, context_branch):
385
 
        # We would have the revno here, but we don't really care
386
 
        branch, revno, revision_id = self._lookup(context_branch, None)
387
 
        return revision_id
388
 
 
 
349
        return RevisionInfo(branch, revno, revision_id)
 
350
        
389
351
    def needs_branch(self):
390
352
        return self.spec.find(':') == -1
391
353
 
412
374
    Examples::
413
375
 
414
376
      revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
415
 
    """
416
 
 
 
377
    """    
417
378
    prefix = 'revid:'
418
379
 
419
380
    def _match_on(self, branch, revs):
423
384
        revision_id = osutils.safe_revision_id(self.spec, warn=False)
424
385
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
425
386
 
426
 
    def _as_revision_id(self, context_branch):
427
 
        return osutils.safe_revision_id(self.spec, warn=False)
428
 
 
429
387
SPEC_TYPES.append(RevisionSpec_revid)
430
388
 
431
389
 
440
398
 
441
399
      last:1        -> return the last revision
442
400
      last:3        -> return the revision 2 before the end.
443
 
    """
 
401
    """    
444
402
 
445
403
    prefix = 'last:'
446
404
 
447
405
    def _match_on(self, branch, revs):
448
 
        revno, revision_id = self._revno_and_revision_id(branch, revs)
449
 
        return RevisionInfo(branch, revno, revision_id)
450
 
 
451
 
    def _revno_and_revision_id(self, context_branch, revs_or_none):
452
 
        last_revno, last_revision_id = context_branch.last_revision_info()
453
 
 
454
406
        if self.spec == '':
455
 
            if not last_revno:
456
 
                raise errors.NoCommits(context_branch)
457
 
            return last_revno, last_revision_id
 
407
            if not revs:
 
408
                raise errors.NoCommits(branch)
 
409
            return RevisionInfo(branch, len(revs), revs[-1])
458
410
 
459
411
        try:
460
412
            offset = int(self.spec)
461
413
        except ValueError, e:
462
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
 
414
            raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
463
415
 
464
416
        if offset <= 0:
465
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
417
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
466
418
                                             'you must supply a positive value')
467
 
 
468
 
        revno = last_revno - offset + 1
 
419
        revno = len(revs) - offset + 1
469
420
        try:
470
 
            revision_id = context_branch.get_rev_id(revno, revs_or_none)
 
421
            revision_id = branch.get_rev_id(revno, revs)
471
422
        except errors.NoSuchRevision:
472
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
473
 
        return revno, revision_id
474
 
 
475
 
    def _as_revision_id(self, context_branch):
476
 
        # We compute the revno as part of the process, but we don't really care
477
 
        # about it.
478
 
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
479
 
        return revision_id
 
423
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
424
        return RevisionInfo(branch, revno, revision_id)
480
425
 
481
426
SPEC_TYPES.append(RevisionSpec_last)
482
427
 
486
431
 
487
432
    help_txt = """Selects the parent of the revision specified.
488
433
 
489
 
    Supply any revision spec to return the parent of that revision.  This is
490
 
    mostly useful when inspecting revisions that are not in the revision history
491
 
    of a branch.
492
 
 
 
434
    Supply any revision spec to return the parent of that revision.
493
435
    It is an error to request the parent of the null revision (before:0).
 
436
    This is mostly useful when inspecting revisions that are not in the
 
437
    revision history of a branch.
494
438
 
495
439
    Examples::
496
440
 
497
441
      before:1913    -> Return the parent of revno 1913 (revno 1912)
498
442
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
499
443
                                            aaaa@bbbb-1234567890
500
 
      bzr diff -r before:1913..1913
501
 
            -> Find the changes between revision 1913 and its parent (1912).
502
 
               (What changes did revision 1913 introduce).
503
 
               This is equivalent to:  bzr diff -c 1913
 
444
      bzr diff -r before:revid:aaaa..revid:aaaa
 
445
            -> Find the changes between revision 'aaaa' and its parent.
 
446
               (what changes did 'aaaa' introduce)
504
447
    """
505
448
 
506
449
    prefix = 'before:'
531
474
                                                 branch)
532
475
        return RevisionInfo(branch, revno, revision_id)
533
476
 
534
 
    def _as_revision_id(self, context_branch):
535
 
        base_revspec = RevisionSpec.from_string(self.spec)
536
 
        base_revision_id = base_revspec.as_revision_id(context_branch)
537
 
        if base_revision_id == revision.NULL_REVISION:
538
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
539
 
                                         'cannot go before the null: revision')
540
 
        context_repo = context_branch.repository
541
 
        context_repo.lock_read()
542
 
        try:
543
 
            parent_map = context_repo.get_parent_map([base_revision_id])
544
 
        finally:
545
 
            context_repo.unlock()
546
 
        if base_revision_id not in parent_map:
547
 
            # Ghost, or unknown revision id
548
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
549
 
                'cannot find the matching revision')
550
 
        parents = parent_map[base_revision_id]
551
 
        if len(parents) < 1:
552
 
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
553
 
                'No parents for revision.')
554
 
        return parents[0]
555
 
 
556
477
SPEC_TYPES.append(RevisionSpec_before)
557
478
 
558
479
 
572
493
            branch.tags.lookup_tag(self.spec),
573
494
            revs)
574
495
 
575
 
    def _as_revision_id(self, context_branch):
576
 
        return context_branch.tags.lookup_tag(self.spec)
577
 
 
578
496
SPEC_TYPES.append(RevisionSpec_tag)
579
497
 
580
498
 
609
527
 
610
528
    One way to display all the changes since yesterday would be::
611
529
 
612
 
        bzr log -r date:yesterday..
 
530
        bzr log -r date:yesterday..-1
613
531
 
614
532
    Examples::
615
533
 
678
596
        finally:
679
597
            branch.unlock()
680
598
        if rev == len(revs):
681
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
 
599
            return RevisionInfo(branch, None)
682
600
        else:
683
601
            return RevisionInfo(branch, rev + 1)
684
602
 
711
629
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
712
630
        return self._find_revision_info(branch, self.spec)
713
631
 
714
 
    def _as_revision_id(self, context_branch):
715
 
        return self._find_revision_id(context_branch, self.spec)
716
 
 
717
632
    @staticmethod
718
633
    def _find_revision_info(branch, other_location):
719
 
        revision_id = RevisionSpec_ancestor._find_revision_id(branch,
720
 
                                                              other_location)
721
 
        try:
722
 
            revno = branch.revision_id_to_revno(revision_id)
723
 
        except errors.NoSuchRevision:
724
 
            revno = None
725
 
        return RevisionInfo(branch, revno, revision_id)
726
 
 
727
 
    @staticmethod
728
 
    def _find_revision_id(branch, other_location):
729
634
        from bzrlib.branch import Branch
730
635
 
731
 
        branch.lock_read()
732
 
        try:
733
 
            revision_a = revision.ensure_null(branch.last_revision())
734
 
            if revision_a == revision.NULL_REVISION:
735
 
                raise errors.NoCommits(branch)
736
 
            other_branch = Branch.open(other_location)
737
 
            other_branch.lock_read()
738
 
            try:
739
 
                revision_b = revision.ensure_null(other_branch.last_revision())
740
 
                if revision_b == revision.NULL_REVISION:
741
 
                    raise errors.NoCommits(other_branch)
742
 
                graph = branch.repository.get_graph(other_branch.repository)
743
 
                rev_id = graph.find_unique_lca(revision_a, revision_b)
744
 
            finally:
745
 
                other_branch.unlock()
 
636
        other_branch = Branch.open(other_location)
 
637
        revision_a = branch.last_revision()
 
638
        revision_b = other_branch.last_revision()
 
639
        for r, b in ((revision_a, branch), (revision_b, other_branch)):
 
640
            if r in (None, revision.NULL_REVISION):
 
641
                raise errors.NoCommits(b)
 
642
        revision_source = revision.MultipleRevisionSources(
 
643
                branch.repository, other_branch.repository)
 
644
        graph = branch.repository.get_graph(other_branch.repository)
 
645
        revision_a = revision.ensure_null(revision_a)
 
646
        revision_b = revision.ensure_null(revision_b)
 
647
        if revision.NULL_REVISION in (revision_a, revision_b):
 
648
            rev_id = revision.NULL_REVISION
 
649
        else:
 
650
            rev_id = graph.find_unique_lca(revision_a, revision_b)
746
651
            if rev_id == revision.NULL_REVISION:
747
652
                raise errors.NoCommonAncestor(revision_a, revision_b)
748
 
            return rev_id
749
 
        finally:
750
 
            branch.unlock()
 
653
        try:
 
654
            revno = branch.revision_id_to_revno(rev_id)
 
655
        except errors.NoSuchRevision:
 
656
            revno = None
 
657
        return RevisionInfo(branch, revno, rev_id)
751
658
 
752
659
 
753
660
SPEC_TYPES.append(RevisionSpec_ancestor)
779
686
        except errors.NoSuchRevision:
780
687
            revno = None
781
688
        return RevisionInfo(branch, revno, revision_b)
782
 
 
783
 
    def _as_revision_id(self, context_branch):
784
 
        from bzrlib.branch import Branch
785
 
        other_branch = Branch.open(self.spec)
786
 
        last_revision = other_branch.last_revision()
787
 
        last_revision = revision.ensure_null(last_revision)
788
 
        context_branch.fetch(other_branch, last_revision)
789
 
        if last_revision == revision.NULL_REVISION:
790
 
            raise errors.NoCommits(other_branch)
791
 
        return last_revision
792
 
 
793
 
    def _as_tree(self, context_branch):
794
 
        from bzrlib.branch import Branch
795
 
        other_branch = Branch.open(self.spec)
796
 
        last_revision = other_branch.last_revision()
797
 
        last_revision = revision.ensure_null(last_revision)
798
 
        if last_revision == revision.NULL_REVISION:
799
 
            raise errors.NoCommits(other_branch)
800
 
        return other_branch.repository.revision_tree(last_revision)
801
 
 
 
689
        
802
690
SPEC_TYPES.append(RevisionSpec_branch)
803
691
 
804
692
 
809
697
 
810
698
    Diffing against this shows all the changes that were made in this branch,
811
699
    and is a good predictor of what merge will do.  The submit branch is
812
 
    used by the bundle and merge directive commands.  If no submit branch
 
700
    used by the bundle and merge directive comands.  If no submit branch
813
701
    is specified, the parent branch is used instead.
814
702
 
815
703
    The common ancestor is the last revision that existed in both
823
711
 
824
712
    prefix = 'submit:'
825
713
 
826
 
    def _get_submit_location(self, branch):
 
714
    def _match_on(self, branch, revs):
 
715
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
827
716
        submit_location = branch.get_submit_branch()
828
717
        location_type = 'submit branch'
829
718
        if submit_location is None:
832
721
        if submit_location is None:
833
722
            raise errors.NoSubmitBranch(branch)
834
723
        trace.note('Using %s %s', location_type, submit_location)
835
 
        return submit_location
836
 
 
837
 
    def _match_on(self, branch, revs):
838
 
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
839
 
        return self._find_revision_info(branch,
840
 
            self._get_submit_location(branch))
841
 
 
842
 
    def _as_revision_id(self, context_branch):
843
 
        return self._find_revision_id(context_branch,
844
 
            self._get_submit_location(context_branch))
 
724
        return self._find_revision_info(branch, submit_location)
845
725
 
846
726
 
847
727
SPEC_TYPES.append(RevisionSpec_submit)