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
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
650
rev_id = graph.find_unique_lca(revision_a, revision_b)
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)
730
rev_id = graph.find_unique_lca(revision_a, revision_b)
732
other_branch.unlock()
651
733
if rev_id == revision.NULL_REVISION:
652
734
raise errors.NoCommonAncestor(revision_a, revision_b)
654
revno = branch.revision_id_to_revno(rev_id)
655
except errors.NoSuchRevision:
657
return RevisionInfo(branch, revno, rev_id)
660
740
SPEC_TYPES.append(RevisionSpec_ancestor)
686
766
except errors.NoSuchRevision:
688
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)
690
780
SPEC_TYPES.append(RevisionSpec_branch)
698
788
Diffing against this shows all the changes that were made in this branch,
699
789
and is a good predictor of what merge will do. The submit branch is
700
used by the bundle and merge directive comands. If no submit branch
790
used by the bundle and merge directive commands. If no submit branch
701
791
is specified, the parent branch is used instead.
703
793
The common ancestor is the last revision that existed in both
712
802
prefix = 'submit:'
714
def _match_on(self, branch, revs):
715
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
804
def _get_submit_location(self, branch):
716
805
submit_location = branch.get_submit_branch()
717
806
location_type = 'submit branch'
718
807
if submit_location is None:
721
810
if submit_location is None:
722
811
raise errors.NoSubmitBranch(branch)
723
812
trace.note('Using %s %s', location_type, submit_location)
724
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))
727
825
SPEC_TYPES.append(RevisionSpec_submit)