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)
217
218
def in_history(self, branch):
219
if self.wants_revision_history:
220
revs = branch.revision_history()
220
revs = branch.revision_history()
224
222
# this should never trigger.
225
223
# TODO: make it a deprecated code path. RBC 20060928
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__,
284
266
A negative number will count from the end of the branch (-1 is the
285
267
last revision, -2 the previous one). If the negative number is larger
286
268
than the branch's history, the first revision is returned.
289
270
revno:1 -> return the first revision
290
271
revno:3:/path/to/branch -> return the 3rd revision of
291
272
the branch '/path/to/branch'
296
277
your history is very long.
298
279
prefix = 'revno:'
299
wants_revision_history = False
301
281
def _match_on(self, branch, revs):
302
282
"""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
283
loc = self.spec.find(':')
309
285
revno_spec = self.spec
338
314
# the branch object.
339
315
from bzrlib.branch import Branch
340
316
branch = Branch.open(branch_spec)
317
# Need to use a new revision history
318
# because we are using a specific branch
319
revs = branch.revision_history()
344
322
branch.lock_read()
346
revision_id_to_revno = branch.get_revision_id_to_revno_map()
347
revisions = [revision_id for revision_id, revno
348
in revision_id_to_revno.iteritems()
349
if revno == match_revno]
324
last_rev = branch.last_revision()
325
merge_sorted_revisions = tsort.merge_sort(
326
branch.repository.get_revision_graph(last_rev),
330
return item[3] == match_revno
331
revisions = filter(match, merge_sorted_revisions)
352
334
if len(revisions) != 1:
353
return branch, None, None
335
return RevisionInfo(branch, None, None)
355
337
# there is no traditional 'revno' for dotted-decimal revnos.
356
338
# so for API compatability we return None.
357
return branch, None, revisions[0]
339
return RevisionInfo(branch, None, revisions[0][1])
359
last_revno, last_revision_id = branch.last_revision_info()
361
342
# if get_rev_id supported negative revnos, there would not be a
362
343
# need for this special case.
363
if (-revno) >= last_revno:
344
if (-revno) >= len(revs):
366
revno = last_revno + revno + 1
347
revno = len(revs) + revno + 1
368
revision_id = branch.get_rev_id(revno, revs_or_none)
349
revision_id = branch.get_rev_id(revno, revs)
369
350
except errors.NoSuchRevision:
370
351
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)
352
return RevisionInfo(branch, revno, revision_id)
378
354
def needs_branch(self):
379
355
return self.spec.find(':') == -1
398
374
Supply a specific revision id, that can be used to specify any
399
375
revision id in the ancestry of the branch.
400
376
Including merges, and pending merges.
403
378
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
406
380
prefix = 'revid:'
408
382
def _match_on(self, branch, revs):
412
386
revision_id = osutils.safe_revision_id(self.spec, warn=False)
413
387
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
389
SPEC_TYPES.append(RevisionSpec_revid)
426
397
Supply a positive number to get the nth revision from the end.
427
398
This is the same as supplying negative numbers to the 'revno:' spec.
430
400
last:1 -> return the last revision
431
401
last:3 -> return the revision 2 before the end.
436
406
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
407
if self.spec == '':
445
raise errors.NoCommits(context_branch)
446
return last_revno, last_revision_id
409
raise errors.NoCommits(branch)
410
return RevisionInfo(branch, len(revs), revs[-1])
449
413
offset = int(self.spec)
450
414
except ValueError, e:
451
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
415
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
454
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
418
raise errors.InvalidRevisionSpec(self.user_spec, branch,
455
419
'you must supply a positive value')
457
revno = last_revno - offset + 1
420
revno = len(revs) - offset + 1
459
revision_id = context_branch.get_rev_id(revno, revs_or_none)
422
revision_id = branch.get_rev_id(revno, revs)
460
423
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)
424
raise errors.InvalidRevisionSpec(self.user_spec, branch)
425
return RevisionInfo(branch, revno, revision_id)
470
427
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)
594
525
Matches the first entry after a given date (either at midnight or
595
526
at a specified time).
597
One way to display all the changes since yesterday would be::
599
bzr log -r date:yesterday..
528
One way to display all the changes since yesterday would be:
529
bzr log -r date:yesterday..-1
603
532
date:yesterday -> select the first revision since yesterday
604
533
date:2006-08-14,17:10:14 -> select the first revision after
605
534
August 14th, 2006 at 5:10pm.
698
626
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
699
627
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
630
def _find_revision_info(branch, other_location):
706
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
631
from bzrlib.branch import Branch
633
other_branch = Branch.open(other_location)
634
revision_a = branch.last_revision()
635
revision_b = other_branch.last_revision()
636
for r, b in ((revision_a, branch), (revision_b, other_branch)):
637
if r in (None, revision.NULL_REVISION):
638
raise errors.NoCommits(b)
639
revision_source = revision.MultipleRevisionSources(
640
branch.repository, other_branch.repository)
641
rev_id = revision.common_ancestor(revision_a, revision_b,
709
revno = branch.revision_id_to_revno(revision_id)
644
revno = branch.revision_id_to_revno(rev_id)
710
645
except errors.NoSuchRevision:
712
return RevisionInfo(branch, revno, revision_id)
715
def _find_revision_id(branch, other_location):
716
from bzrlib.branch import Branch
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()
733
if rev_id == revision.NULL_REVISION:
734
raise errors.NoCommonAncestor(revision_a, revision_b)
647
return RevisionInfo(branch, revno, rev_id)
740
650
SPEC_TYPES.append(RevisionSpec_ancestor)
766
675
except errors.NoSuchRevision:
768
677
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
679
SPEC_TYPES.append(RevisionSpec_branch)
788
687
Diffing against this shows all the changes that were made in this branch,
789
688
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
689
used by the bundle and merge directive comands. If no submit branch
791
690
is specified, the parent branch is used instead.
793
692
The common ancestor is the last revision that existed in both
794
693
branches. Usually this is the branch point, but it could also be
795
694
a revision that was merged.
799
697
$ bzr diff -r submit:
802
700
prefix = 'submit:'
804
def _get_submit_location(self, branch):
702
def _match_on(self, branch, revs):
703
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
805
704
submit_location = branch.get_submit_branch()
806
705
location_type = 'submit branch'
807
706
if submit_location is None:
810
709
if submit_location is None:
811
710
raise errors.NoSubmitBranch(branch)
812
711
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))
712
return self._find_revision_info(branch, submit_location)
825
715
SPEC_TYPES.append(RevisionSpec_submit)