163
160
return RevisionSpec(None, _internal=True)
162
assert isinstance(spec, basestring), \
163
"You should only supply strings not %s" % (type(spec),)
164
165
for spectype in SPEC_TYPES:
165
166
if spec.startswith(spectype.prefix):
166
167
trace.mutter('Returning RevisionSpec %s for %s',
201
202
def _match_on(self, branch, revs):
202
203
trace.mutter('Returning RevisionSpec._match_on: None')
203
return RevisionInfo(branch, None, None)
204
return RevisionInfo(branch, 0, None)
205
206
def _match_on_and_check(self, branch, revs):
206
207
info = self._match_on(branch, revs)
209
elif info == (None, None):
210
# special case - nothing supplied
210
elif info == (0, None):
211
# special case - the empty tree
212
213
elif self.prefix:
213
214
raise errors.InvalidRevisionSpec(self.user_spec, branch)
235
233
# will do what you expect.
236
234
in_store = in_history
237
235
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
255
237
def __repr__(self):
256
238
# this is mostly for helping with testing
257
239
return '<%s %s>' % (self.__class__.__name__,
296
278
your history is very long.
298
280
prefix = 'revno:'
299
wants_revision_history = False
301
282
def _match_on(self, branch, revs):
302
283
"""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):
307
284
loc = self.spec.find(':')
309
286
revno_spec = self.spec
352
331
if len(revisions) != 1:
353
return branch, None, None
332
return RevisionInfo(branch, None, None)
355
334
# there is no traditional 'revno' for dotted-decimal revnos.
356
335
# so for API compatability we return None.
357
return branch, None, revisions[0]
336
return RevisionInfo(branch, None, revisions[0])
359
last_revno, last_revision_id = branch.last_revision_info()
361
339
# if get_rev_id supported negative revnos, there would not be a
362
340
# need for this special case.
363
if (-revno) >= last_revno:
341
if (-revno) >= len(revs):
366
revno = last_revno + revno + 1
344
revno = len(revs) + revno + 1
368
revision_id = branch.get_rev_id(revno, revs_or_none)
346
revision_id = branch.get_rev_id(revno, revs)
369
347
except errors.NoSuchRevision:
370
348
raise errors.InvalidRevisionSpec(self.user_spec, branch)
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)
349
return RevisionInfo(branch, revno, revision_id)
378
351
def needs_branch(self):
379
352
return self.spec.find(':') == -1
412
384
revision_id = osutils.safe_revision_id(self.spec, warn=False)
413
385
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)
418
387
SPEC_TYPES.append(RevisionSpec_revid)
430
399
last:1 -> return the last revision
431
400
last:3 -> return the revision 2 before the end.
436
405
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()
443
406
if self.spec == '':
445
raise errors.NoCommits(context_branch)
446
return last_revno, last_revision_id
408
raise errors.NoCommits(branch)
409
return RevisionInfo(branch, len(revs), revs[-1])
449
412
offset = int(self.spec)
450
413
except ValueError, e:
451
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
414
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
454
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
417
raise errors.InvalidRevisionSpec(self.user_spec, branch,
455
418
'you must supply a positive value')
457
revno = last_revno - offset + 1
419
revno = len(revs) - offset + 1
459
revision_id = context_branch.get_rev_id(revno, revs_or_none)
421
revision_id = branch.get_rev_id(revno, revs)
460
422
except errors.NoSuchRevision:
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)
423
raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
return RevisionInfo(branch, revno, revision_id)
470
426
SPEC_TYPES.append(RevisionSpec_last)
519
475
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.')
543
477
SPEC_TYPES.append(RevisionSpec_before)
698
629
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
699
630
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)
705
633
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):
716
634
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)
718
642
branch.lock_read()
643
other_branch.lock_read()
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)
645
revision_source = revision.MultipleRevisionSources(
646
branch.repository, other_branch.repository)
647
graph = branch.repository.get_graph(other_branch.repository)
648
revision_a = revision.ensure_null(revision_a)
649
revision_b = revision.ensure_null(revision_b)
650
if revision.NULL_REVISION in (revision_a, revision_b):
651
rev_id = revision.NULL_REVISION
730
653
rev_id = graph.find_unique_lca(revision_a, revision_b)
732
other_branch.unlock()
733
if rev_id == revision.NULL_REVISION:
734
raise errors.NoCommonAncestor(revision_a, revision_b)
654
if rev_id == revision.NULL_REVISION:
655
raise errors.NoCommonAncestor(revision_a, revision_b)
657
revno = branch.revision_id_to_revno(rev_id)
658
except errors.NoSuchRevision:
660
return RevisionInfo(branch, revno, rev_id)
663
other_branch.unlock()
740
666
SPEC_TYPES.append(RevisionSpec_ancestor)
766
692
except errors.NoSuchRevision:
768
694
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)
780
696
SPEC_TYPES.append(RevisionSpec_branch)
788
704
Diffing against this shows all the changes that were made in this branch,
789
705
and is a good predictor of what merge will do. The submit branch is
790
used by the bundle and merge directive commands. If no submit branch
706
used by the bundle and merge directive comands. If no submit branch
791
707
is specified, the parent branch is used instead.
793
709
The common ancestor is the last revision that existed in both
802
718
prefix = 'submit:'
804
def _get_submit_location(self, branch):
720
def _match_on(self, branch, revs):
721
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
805
722
submit_location = branch.get_submit_branch()
806
723
location_type = 'submit branch'
807
724
if submit_location is None:
810
727
if submit_location is None:
811
728
raise errors.NoSubmitBranch(branch)
812
729
trace.note('Using %s %s', location_type, 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))
730
return self._find_revision_info(branch, submit_location)
825
733
SPEC_TYPES.append(RevisionSpec_submit)