160
163
return RevisionSpec(None, _internal=True)
162
assert isinstance(spec, basestring), \
163
"You should only supply strings not %s" % (type(spec),)
165
164
for spectype in SPEC_TYPES:
166
165
if spec.startswith(spectype.prefix):
167
166
trace.mutter('Returning RevisionSpec %s for %s',
202
201
def _match_on(self, branch, revs):
203
202
trace.mutter('Returning RevisionSpec._match_on: None')
204
return RevisionInfo(branch, 0, None)
203
return RevisionInfo(branch, None, None)
206
205
def _match_on_and_check(self, branch, revs):
207
206
info = self._match_on(branch, revs)
210
elif info == (0, None):
211
# special case - the empty tree
209
elif info == (None, None):
210
# special case - nothing supplied
213
212
elif self.prefix:
214
213
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
239
def as_revision_id(self, context_branch):
240
"""Return just the revision_id for this revisions spec.
242
Some revision specs require a context_branch to be able to determine
243
their value. Not all specs will make use of it.
245
return self._as_revision_id(context_branch)
247
def _as_revision_id(self, context_branch):
248
"""Implementation of as_revision_id()
250
Classes should override this function to provide appropriate
251
functionality. The default is to just call '.in_history().rev_id'
253
return self.in_history(context_branch).rev_id
237
255
def __repr__(self):
238
256
# this is mostly for helping with testing
239
257
return '<%s %s>' % (self.__class__.__name__,
278
296
your history is very long.
280
298
prefix = 'revno:'
299
wants_revision_history = False
282
301
def _match_on(self, branch, revs):
283
302
"""Lookup a revision by revision number"""
303
branch, revno, revision_id = self._lookup(branch, revs)
304
return RevisionInfo(branch, revno, revision_id)
306
def _lookup(self, branch, revs_or_none):
284
307
loc = self.spec.find(':')
286
309
revno_spec = self.spec
331
352
if len(revisions) != 1:
332
return RevisionInfo(branch, None, None)
353
return branch, None, None
334
355
# there is no traditional 'revno' for dotted-decimal revnos.
335
356
# so for API compatability we return None.
336
return RevisionInfo(branch, None, revisions[0])
357
return branch, None, revisions[0]
359
last_revno, last_revision_id = branch.last_revision_info()
339
361
# if get_rev_id supported negative revnos, there would not be a
340
362
# need for this special case.
341
if (-revno) >= len(revs):
363
if (-revno) >= last_revno:
344
revno = len(revs) + revno + 1
366
revno = last_revno + revno + 1
346
revision_id = branch.get_rev_id(revno, revs)
368
revision_id = branch.get_rev_id(revno, revs_or_none)
347
369
except errors.NoSuchRevision:
348
370
raise errors.InvalidRevisionSpec(self.user_spec, branch)
349
return RevisionInfo(branch, revno, revision_id)
371
return branch, revno, revision_id
373
def _as_revision_id(self, context_branch):
374
# We would have the revno here, but we don't really care
375
branch, revno, revision_id = self._lookup(context_branch, None)
351
378
def needs_branch(self):
352
379
return self.spec.find(':') == -1
384
412
revision_id = osutils.safe_revision_id(self.spec, warn=False)
385
413
return RevisionInfo.from_revision_id(branch, revision_id, revs)
415
def _as_revision_id(self, context_branch):
416
return osutils.safe_revision_id(self.spec, warn=False)
387
418
SPEC_TYPES.append(RevisionSpec_revid)
399
430
last:1 -> return the last revision
400
431
last:3 -> return the revision 2 before the end.
405
436
def _match_on(self, branch, revs):
437
revno, revision_id = self._revno_and_revision_id(branch, revs)
438
return RevisionInfo(branch, revno, revision_id)
440
def _revno_and_revision_id(self, context_branch, revs_or_none):
441
last_revno, last_revision_id = context_branch.last_revision_info()
406
443
if self.spec == '':
408
raise errors.NoCommits(branch)
409
return RevisionInfo(branch, len(revs), revs[-1])
445
raise errors.NoCommits(context_branch)
446
return last_revno, last_revision_id
412
449
offset = int(self.spec)
413
450
except ValueError, e:
414
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
451
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
417
raise errors.InvalidRevisionSpec(self.user_spec, branch,
454
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
418
455
'you must supply a positive value')
419
revno = len(revs) - offset + 1
457
revno = last_revno - offset + 1
421
revision_id = branch.get_rev_id(revno, revs)
459
revision_id = context_branch.get_rev_id(revno, revs_or_none)
422
460
except errors.NoSuchRevision:
423
raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
return RevisionInfo(branch, revno, revision_id)
461
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
462
return revno, revision_id
464
def _as_revision_id(self, context_branch):
465
# We compute the revno as part of the process, but we don't really care
467
revno, revision_id = self._revno_and_revision_id(context_branch, None)
426
470
SPEC_TYPES.append(RevisionSpec_last)
475
519
return RevisionInfo(branch, revno, revision_id)
521
def _as_revision_id(self, context_branch):
522
base_revspec = RevisionSpec.from_string(self.spec)
523
base_revision_id = base_revspec.as_revision_id(context_branch)
524
if base_revision_id == revision.NULL_REVISION:
525
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
526
'cannot go before the null: revision')
527
context_repo = context_branch.repository
528
context_repo.lock_read()
530
parent_map = context_repo.get_parent_map([base_revision_id])
532
context_repo.unlock()
533
if base_revision_id not in parent_map:
534
# Ghost, or unknown revision id
535
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
536
'cannot find the matching revision')
537
parents = parent_map[base_revision_id]
539
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
540
'No parents for revision.')
477
543
SPEC_TYPES.append(RevisionSpec_before)
629
698
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
630
699
return self._find_revision_info(branch, self.spec)
701
def _as_revision_id(self, context_branch):
702
return self._find_revision_id(context_branch, self.spec)
633
705
def _find_revision_info(branch, other_location):
706
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
709
revno = branch.revision_id_to_revno(revision_id)
710
except errors.NoSuchRevision:
712
return RevisionInfo(branch, revno, revision_id)
715
def _find_revision_id(branch, other_location):
634
716
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
718
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
720
revision_a = revision.ensure_null(branch.last_revision())
721
if revision_a == revision.NULL_REVISION:
722
raise errors.NoCommits(branch)
723
other_branch = Branch.open(other_location)
724
other_branch.lock_read()
726
revision_b = revision.ensure_null(other_branch.last_revision())
727
if revision_b == revision.NULL_REVISION:
728
raise errors.NoCommits(other_branch)
729
graph = branch.repository.get_graph(other_branch.repository)
651
730
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)
732
other_branch.unlock()
733
if rev_id == revision.NULL_REVISION:
734
raise errors.NoCommonAncestor(revision_a, revision_b)
661
other_branch.unlock()
664
740
SPEC_TYPES.append(RevisionSpec_ancestor)
690
766
except errors.NoSuchRevision:
692
768
return RevisionInfo(branch, revno, revision_b)
770
def _as_revision_id(self, context_branch):
771
from bzrlib.branch import Branch
772
other_branch = Branch.open(self.spec)
773
last_revision = other_branch.last_revision()
774
last_revision = revision.ensure_null(last_revision)
775
context_branch.fetch(other_branch, last_revision)
776
if last_revision == revision.NULL_REVISION:
777
raise errors.NoCommits(other_branch)
694
780
SPEC_TYPES.append(RevisionSpec_branch)
716
802
prefix = 'submit:'
718
def _match_on(self, branch, revs):
719
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
804
def _get_submit_location(self, branch):
720
805
submit_location = branch.get_submit_branch()
721
806
location_type = 'submit branch'
722
807
if submit_location is None:
725
810
if submit_location is None:
726
811
raise errors.NoSubmitBranch(branch)
727
812
trace.note('Using %s %s', location_type, submit_location)
728
return self._find_revision_info(branch, submit_location)
813
return submit_location
815
def _match_on(self, branch, revs):
816
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
817
return self._find_revision_info(branch,
818
self._get_submit_location(branch))
820
def _as_revision_id(self, context_branch):
821
return self._find_revision_id(context_branch,
822
self._get_submit_location(context_branch))
731
825
SPEC_TYPES.append(RevisionSpec_submit)