160
162
return RevisionSpec(None, _internal=True)
162
assert isinstance(spec, basestring), \
163
"You should only supply strings not %s" % (type(spec),)
165
163
for spectype in SPEC_TYPES:
166
164
if spec.startswith(spectype.prefix):
167
165
trace.mutter('Returning RevisionSpec %s for %s',
202
200
def _match_on(self, branch, revs):
203
201
trace.mutter('Returning RevisionSpec._match_on: None')
204
return RevisionInfo(branch, 0, None)
202
return RevisionInfo(branch, None, None)
206
204
def _match_on_and_check(self, branch, revs):
207
205
info = self._match_on(branch, revs)
210
elif info == (0, None):
211
# special case - the empty tree
208
elif info == (None, None):
209
# special case - nothing supplied
213
211
elif self.prefix:
214
212
raise errors.InvalidRevisionSpec(self.user_spec, branch)
233
231
# will do what you expect.
234
232
in_store = in_history
235
233
in_branch = in_store
235
def as_revision_id(self, context_branch):
236
"""Return just the revision_id for this revisions spec.
238
Some revision specs require a context_branch to be able to determine
239
their value. Not all specs will make use of it.
241
return self._as_revision_id(context_branch)
243
def _as_revision_id(self, context_branch):
244
"""Implementation of as_revision_id()
246
Classes should override this function to provide appropriate
247
functionality. The default is to just call '.in_history().rev_id'
249
return self.in_history(context_branch).rev_id
237
251
def __repr__(self):
238
252
# this is mostly for helping with testing
239
253
return '<%s %s>' % (self.__class__.__name__,
282
296
def _match_on(self, branch, revs):
283
297
"""Lookup a revision by revision number"""
298
branch, revno, revision_id = self._lookup(branch, revs)
299
return RevisionInfo(branch, revno, revision_id)
301
def _lookup(self, branch, revs_or_none):
284
302
loc = self.spec.find(':')
286
304
revno_spec = self.spec
331
347
if len(revisions) != 1:
332
return RevisionInfo(branch, None, None)
348
return branch, None, None
334
350
# there is no traditional 'revno' for dotted-decimal revnos.
335
351
# so for API compatability we return None.
336
return RevisionInfo(branch, None, revisions[0])
352
return branch, None, revisions[0]
354
last_revno, last_revision_id = branch.last_revision_info()
339
356
# if get_rev_id supported negative revnos, there would not be a
340
357
# need for this special case.
341
if (-revno) >= len(revs):
358
if (-revno) >= last_revno:
344
revno = len(revs) + revno + 1
361
revno = last_revno + revno + 1
346
revision_id = branch.get_rev_id(revno, revs)
363
revision_id = branch.get_rev_id(revno, revs_or_none)
347
364
except errors.NoSuchRevision:
348
365
raise errors.InvalidRevisionSpec(self.user_spec, branch)
349
return RevisionInfo(branch, revno, revision_id)
366
return branch, revno, revision_id
368
def _as_revision_id(self, context_branch):
369
# We would have the revno here, but we don't really care
370
branch, revno, revision_id = self._lookup(context_branch, None)
351
373
def needs_branch(self):
352
374
return self.spec.find(':') == -1
384
407
revision_id = osutils.safe_revision_id(self.spec, warn=False)
385
408
return RevisionInfo.from_revision_id(branch, revision_id, revs)
410
def _as_revision_id(self, context_branch):
411
return osutils.safe_revision_id(self.spec, warn=False)
387
413
SPEC_TYPES.append(RevisionSpec_revid)
399
425
last:1 -> return the last revision
400
426
last:3 -> return the revision 2 before the end.
405
431
def _match_on(self, branch, revs):
432
revno, revision_id = self._revno_and_revision_id(branch, revs)
433
return RevisionInfo(branch, revno, revision_id)
435
def _revno_and_revision_id(self, context_branch, revs_or_none):
436
last_revno, last_revision_id = context_branch.last_revision_info()
406
438
if self.spec == '':
408
raise errors.NoCommits(branch)
409
return RevisionInfo(branch, len(revs), revs[-1])
440
raise errors.NoCommits(context_branch)
441
return last_revno, last_revision_id
412
444
offset = int(self.spec)
413
445
except ValueError, e:
414
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
446
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
417
raise errors.InvalidRevisionSpec(self.user_spec, branch,
449
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
418
450
'you must supply a positive value')
419
revno = len(revs) - offset + 1
452
revno = last_revno - offset + 1
421
revision_id = branch.get_rev_id(revno, revs)
454
revision_id = context_branch.get_rev_id(revno, revs_or_none)
422
455
except errors.NoSuchRevision:
423
raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
return RevisionInfo(branch, revno, revision_id)
456
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
457
return revno, revision_id
459
def _as_revision_id(self, context_branch):
460
# We compute the revno as part of the process, but we don't really care
462
revno, revision_id = self._revno_and_revision_id(context_branch, None)
426
465
SPEC_TYPES.append(RevisionSpec_last)
475
514
return RevisionInfo(branch, revno, revision_id)
516
def _as_revision_id(self, context_branch):
517
base_revspec = RevisionSpec.from_string(self.spec)
518
base_revision_id = base_revspec.as_revision_id(context_branch)
519
if base_revision_id == revision.NULL_REVISION:
520
raise errors.InvalidRevisionSpec(self.user_spec, branch,
521
'cannot go before the null: revision')
522
context_repo = context_branch.repository
523
context_repo.lock_read()
525
parent_map = context_repo.get_parent_map([base_revision_id])
527
context_repo.unlock()
528
if base_revision_id not in parent_map:
529
# Ghost, or unknown revision id
530
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
531
'cannot find the matching revision')
532
parents = parent_map[base_revision_id]
534
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
535
'No parents for revision.')
477
538
SPEC_TYPES.append(RevisionSpec_before)
629
693
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
630
694
return self._find_revision_info(branch, self.spec)
696
def _as_revision_id(self, context_branch):
697
return self._find_revision_id(context_branch, self.spec)
633
700
def _find_revision_info(branch, other_location):
701
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
704
revno = branch.revision_id_to_revno(revision_id)
705
except errors.NoSuchRevision:
707
return RevisionInfo(branch, revno, revision_id)
710
def _find_revision_id(branch, other_location):
634
711
from bzrlib.branch import Branch
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
713
branch.lock_read()
643
other_branch.lock_read()
645
graph = branch.repository.get_graph(other_branch.repository)
646
revision_a = revision.ensure_null(revision_a)
647
revision_b = revision.ensure_null(revision_b)
648
if revision.NULL_REVISION in (revision_a, revision_b):
649
rev_id = revision.NULL_REVISION
715
revision_a = revision.ensure_null(branch.last_revision())
716
if revision_a == revision.NULL_REVISION:
717
raise errors.NoCommits(branch)
718
other_branch = Branch.open(other_location)
719
other_branch.lock_read()
721
revision_b = revision.ensure_null(other_branch.last_revision())
722
if revision_b == revision.NULL_REVISION:
723
raise errors.NoCommits(other_branch)
724
graph = branch.repository.get_graph(other_branch.repository)
651
725
rev_id = graph.find_unique_lca(revision_a, revision_b)
652
if rev_id == revision.NULL_REVISION:
653
raise errors.NoCommonAncestor(revision_a, revision_b)
655
revno = branch.revision_id_to_revno(rev_id)
656
except errors.NoSuchRevision:
658
return RevisionInfo(branch, revno, rev_id)
727
other_branch.unlock()
728
if rev_id == revision.NULL_REVISION:
729
raise errors.NoCommonAncestor(revision_a, revision_b)
661
other_branch.unlock()
664
735
SPEC_TYPES.append(RevisionSpec_ancestor)
690
761
except errors.NoSuchRevision:
692
763
return RevisionInfo(branch, revno, revision_b)
765
def _as_revision_id(self, context_branch):
766
from bzrlib.branch import Branch
767
other_branch = Branch.open(self.spec)
768
last_revision = other_branch.last_revision()
769
last_revision = revision.ensure_null(last_revision)
770
context_branch.fetch(other_branch, last_revision)
771
if last_revision == revision.NULL_REVISION:
772
raise errors.NoCommits(other_branch)
694
775
SPEC_TYPES.append(RevisionSpec_branch)
716
797
prefix = 'submit:'
718
def _match_on(self, branch, revs):
719
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
799
def _get_submit_location(self, branch):
720
800
submit_location = branch.get_submit_branch()
721
801
location_type = 'submit branch'
722
802
if submit_location is None:
725
805
if submit_location is None:
726
806
raise errors.NoSubmitBranch(branch)
727
807
trace.note('Using %s %s', location_type, submit_location)
728
return self._find_revision_info(branch, submit_location)
808
return submit_location
810
def _match_on(self, branch, revs):
811
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
812
return self._find_revision_info(branch,
813
self._get_submit_location(branch))
815
def _as_revision_id(self, context_branch):
816
return self._find_revision_id(context_branch,
817
self._get_submit_location(context_branch))
731
820
SPEC_TYPES.append(RevisionSpec_submit)