~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
100
100
 
101
101
        Use this if you don't know or care what the revno is.
102
102
        """
 
103
        if revision_id == revision.NULL_REVISION:
 
104
            return RevisionInfo(branch, 0, revision_id)
103
105
        try:
104
106
            revno = revs.index(revision_id) + 1
105
107
        except ValueError:
201
203
 
202
204
    def _match_on(self, branch, revs):
203
205
        trace.mutter('Returning RevisionSpec._match_on: None')
204
 
        return RevisionInfo(branch, 0, None)
 
206
        return RevisionInfo(branch, None, None)
205
207
 
206
208
    def _match_on_and_check(self, branch, revs):
207
209
        info = self._match_on(branch, revs)
208
210
        if info:
209
211
            return info
210
 
        elif info == (0, None):
211
 
            # special case - the empty tree
 
212
        elif info == (None, None):
 
213
            # special case - nothing supplied
212
214
            return info
213
215
        elif self.prefix:
214
216
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
233
235
    # will do what you expect.
234
236
    in_store = in_history
235
237
    in_branch = in_store
236
 
        
 
238
 
 
239
    def as_revision_id(self, context_branch):
 
240
        """Return just the revision_id for this revisions spec.
 
241
 
 
242
        Some revision specs require a context_branch to be able to determine
 
243
        their value. Not all specs will make use of it.
 
244
        """
 
245
        return self._as_revision_id(context_branch)
 
246
 
 
247
    def _as_revision_id(self, context_branch):
 
248
        """Implementation of as_revision_id()
 
249
 
 
250
        Classes should override this function to provide appropriate
 
251
        functionality. The default is to just call '.in_history().rev_id'
 
252
        """
 
253
        return self.in_history(context_branch).rev_id
 
254
 
237
255
    def __repr__(self):
238
256
        # this is mostly for helping with testing
239
257
        return '<%s %s>' % (self.__class__.__name__,
281
299
 
282
300
    def _match_on(self, branch, revs):
283
301
        """Lookup a revision by revision number"""
 
302
        branch, revno, revision_id = self._lookup(branch, revs)
 
303
        return RevisionInfo(branch, revno, revision_id)
 
304
 
 
305
    def _lookup(self, branch, revs_or_none):
284
306
        loc = self.spec.find(':')
285
307
        if loc == -1:
286
308
            revno_spec = self.spec
315
337
            # the branch object.
316
338
            from bzrlib.branch import Branch
317
339
            branch = Branch.open(branch_spec)
318
 
            # Need to use a new revision history
319
 
            # because we are using a specific branch
320
 
            revs = branch.revision_history()
 
340
            revs_or_none = None
321
341
 
322
342
        if dotted:
323
343
            branch.lock_read()
329
349
            finally:
330
350
                branch.unlock()
331
351
            if len(revisions) != 1:
332
 
                return RevisionInfo(branch, None, None)
 
352
                return branch, None, None
333
353
            else:
334
354
                # there is no traditional 'revno' for dotted-decimal revnos.
335
355
                # so for  API compatability we return None.
336
 
                return RevisionInfo(branch, None, revisions[0])
 
356
                return branch, None, revisions[0]
337
357
        else:
 
358
            last_revno, last_revision_id = branch.last_revision_info()
338
359
            if revno < 0:
339
360
                # if get_rev_id supported negative revnos, there would not be a
340
361
                # need for this special case.
341
 
                if (-revno) >= len(revs):
 
362
                if (-revno) >= last_revno:
342
363
                    revno = 1
343
364
                else:
344
 
                    revno = len(revs) + revno + 1
 
365
                    revno = last_revno + revno + 1
345
366
            try:
346
 
                revision_id = branch.get_rev_id(revno, revs)
 
367
                revision_id = branch.get_rev_id(revno, revs_or_none)
347
368
            except errors.NoSuchRevision:
348
369
                raise errors.InvalidRevisionSpec(self.user_spec, branch)
349
 
        return RevisionInfo(branch, revno, revision_id)
350
 
        
 
370
        return branch, revno, revision_id
 
371
 
 
372
    def _as_revision_id(self, context_branch):
 
373
        # We would have the revno here, but we don't really care
 
374
        branch, revno, revision_id = self._lookup(context_branch, None)
 
375
        return revision_id
 
376
 
351
377
    def needs_branch(self):
352
378
        return self.spec.find(':') == -1
353
379
 
374
400
    Examples::
375
401
 
376
402
      revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
377
 
    """    
 
403
    """
 
404
 
378
405
    prefix = 'revid:'
379
406
 
380
407
    def _match_on(self, branch, revs):
384
411
        revision_id = osutils.safe_revision_id(self.spec, warn=False)
385
412
        return RevisionInfo.from_revision_id(branch, revision_id, revs)
386
413
 
 
414
    def _as_revision_id(self, context_branch):
 
415
        return osutils.safe_revision_id(self.spec, warn=False)
 
416
 
387
417
SPEC_TYPES.append(RevisionSpec_revid)
388
418
 
389
419
 
398
428
 
399
429
      last:1        -> return the last revision
400
430
      last:3        -> return the revision 2 before the end.
401
 
    """    
 
431
    """
402
432
 
403
433
    prefix = 'last:'
404
434
 
405
435
    def _match_on(self, branch, revs):
 
436
        revno, revision_id = self._revno_and_revision_id(branch, revs)
 
437
        return RevisionInfo(branch, revno, revision_id)
 
438
 
 
439
    def _revno_and_revision_id(self, context_branch, revs_or_none):
 
440
        last_revno, last_revision_id = context_branch.last_revision_info()
 
441
 
406
442
        if self.spec == '':
407
 
            if not revs:
408
 
                raise errors.NoCommits(branch)
409
 
            return RevisionInfo(branch, len(revs), revs[-1])
 
443
            if not last_revno:
 
444
                raise errors.NoCommits(context_branch)
 
445
            return last_revno, last_revision_id
410
446
 
411
447
        try:
412
448
            offset = int(self.spec)
413
449
        except ValueError, e:
414
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
 
450
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
415
451
 
416
452
        if offset <= 0:
417
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
 
453
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
418
454
                                             'you must supply a positive value')
419
 
        revno = len(revs) - offset + 1
 
455
 
 
456
        revno = last_revno - offset + 1
420
457
        try:
421
 
            revision_id = branch.get_rev_id(revno, revs)
 
458
            revision_id = context_branch.get_rev_id(revno, revs_or_none)
422
459
        except errors.NoSuchRevision:
423
 
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
 
        return RevisionInfo(branch, revno, revision_id)
 
460
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
 
461
        return revno, revision_id
 
462
 
 
463
    def _as_revision_id(self, context_branch):
 
464
        # We compute the revno as part of the process, but we don't really care
 
465
        # about it.
 
466
        revno, revision_id = self._revno_and_revision_id(context_branch, None)
 
467
        return revision_id
425
468
 
426
469
SPEC_TYPES.append(RevisionSpec_last)
427
470
 
474
517
                                                 branch)
475
518
        return RevisionInfo(branch, revno, revision_id)
476
519
 
 
520
    def _as_revision_id(self, context_branch):
 
521
        base_revspec = RevisionSpec.from_string(self.spec)
 
522
        base_revision_id = base_revspec.as_revision_id(context_branch)
 
523
        if base_revision_id == revision.NULL_REVISION:
 
524
            raise errors.InvalidRevisionSpec(self.user_spec, branch,
 
525
                                         'cannot go before the null: revision')
 
526
        context_repo = context_branch.repository
 
527
        context_repo.lock_read()
 
528
        try:
 
529
            parent_map = context_repo.get_parent_map([base_revision_id])
 
530
        finally:
 
531
            context_repo.unlock()
 
532
        if base_revision_id not in parent_map:
 
533
            # Ghost, or unknown revision id
 
534
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
535
                'cannot find the matching revision')
 
536
        parents = parent_map[base_revision_id]
 
537
        if len(parents) < 1:
 
538
            raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
 
539
                'No parents for revision.')
 
540
        return parents[0]
 
541
 
477
542
SPEC_TYPES.append(RevisionSpec_before)
478
543
 
479
544
 
493
558
            branch.tags.lookup_tag(self.spec),
494
559
            revs)
495
560
 
 
561
    def _as_revision_id(self, context_branch):
 
562
        return context_branch.tags.lookup_tag(self.spec)
 
563
 
496
564
SPEC_TYPES.append(RevisionSpec_tag)
497
565
 
498
566
 
596
664
        finally:
597
665
            branch.unlock()
598
666
        if rev == len(revs):
599
 
            return RevisionInfo(branch, None)
 
667
            raise errors.InvalidRevisionSpec(self.user_spec, branch)
600
668
        else:
601
669
            return RevisionInfo(branch, rev + 1)
602
670
 
629
697
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
630
698
        return self._find_revision_info(branch, self.spec)
631
699
 
 
700
    def _as_revision_id(self, context_branch):
 
701
        return self._find_revision_id(context_branch, self.spec)
 
702
 
632
703
    @staticmethod
633
704
    def _find_revision_info(branch, other_location):
 
705
        revision_id = RevisionSpec_ancestor._find_revision_id(branch,
 
706
                                                              other_location)
 
707
        try:
 
708
            revno = branch.revision_id_to_revno(revision_id)
 
709
        except errors.NoSuchRevision:
 
710
            revno = None
 
711
        return RevisionInfo(branch, revno, revision_id)
 
712
 
 
713
    @staticmethod
 
714
    def _find_revision_id(branch, other_location):
634
715
        from bzrlib.branch import Branch
635
716
 
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)
 
717
        branch.lock_read()
 
718
        try:
 
719
            revision_a = revision.ensure_null(branch.last_revision())
 
720
            if revision_a == revision.NULL_REVISION:
 
721
                raise errors.NoCommits(branch)
 
722
            other_branch = Branch.open(other_location)
 
723
            other_branch.lock_read()
 
724
            try:
 
725
                revision_b = revision.ensure_null(other_branch.last_revision())
 
726
                if revision_b == revision.NULL_REVISION:
 
727
                    raise errors.NoCommits(other_branch)
 
728
                graph = branch.repository.get_graph(other_branch.repository)
 
729
                rev_id = graph.find_unique_lca(revision_a, revision_b)
 
730
            finally:
 
731
                other_branch.unlock()
651
732
            if rev_id == revision.NULL_REVISION:
652
733
                raise errors.NoCommonAncestor(revision_a, revision_b)
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)
 
734
            return rev_id
 
735
        finally:
 
736
            branch.unlock()
658
737
 
659
738
 
660
739
SPEC_TYPES.append(RevisionSpec_ancestor)
686
765
        except errors.NoSuchRevision:
687
766
            revno = None
688
767
        return RevisionInfo(branch, revno, revision_b)
689
 
        
 
768
 
 
769
    def _as_revision_id(self, context_branch):
 
770
        from bzrlib.branch import Branch
 
771
        other_branch = Branch.open(self.spec)
 
772
        last_revision = other_branch.last_revision()
 
773
        last_revision = revision.ensure_null(last_revision)
 
774
        if last_revision == revision.NULL_REVISION:
 
775
            raise errors.NoCommits(other_branch)
 
776
        return last_revision
 
777
 
690
778
SPEC_TYPES.append(RevisionSpec_branch)
691
779
 
692
780
 
711
799
 
712
800
    prefix = 'submit:'
713
801
 
714
 
    def _match_on(self, branch, revs):
715
 
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
 
802
    def _get_submit_location(self, branch):
716
803
        submit_location = branch.get_submit_branch()
717
804
        location_type = 'submit branch'
718
805
        if submit_location is None:
721
808
        if submit_location is None:
722
809
            raise errors.NoSubmitBranch(branch)
723
810
        trace.note('Using %s %s', location_type, submit_location)
724
 
        return self._find_revision_info(branch, submit_location)
 
811
        return submit_location
 
812
 
 
813
    def _match_on(self, branch, revs):
 
814
        trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
 
815
        return self._find_revision_info(branch,
 
816
            self._get_submit_location(branch))
 
817
 
 
818
    def _as_revision_id(self, context_branch):
 
819
        return self._find_revision_id(context_branch,
 
820
            self._get_submit_location(context_branch))
725
821
 
726
822
 
727
823
SPEC_TYPES.append(RevisionSpec_submit)