13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
22
26
from bzrlib import (
137
def __new__(cls, spec, _internal=False):
139
return object.__new__(cls, spec, _internal=_internal)
141
symbol_versioning.warn('Creating a RevisionSpec directly has'
142
' been deprecated in version 0.11. Use'
143
' RevisionSpec.from_string()'
145
DeprecationWarning, stacklevel=2)
146
return RevisionSpec.from_string(spec)
141
wants_revision_history = True
149
144
def from_string(spec):
160
155
return RevisionSpec(None, _internal=True)
162
assert isinstance(spec, basestring), \
163
"You should only supply strings not %s" % (type(spec),)
165
for spectype in SPEC_TYPES:
166
if spec.startswith(spectype.prefix):
167
trace.mutter('Returning RevisionSpec %s for %s',
168
spectype.__name__, spec)
169
return spectype(spec, _internal=True)
156
match = revspec_registry.get_prefix(spec)
157
if match is not None:
158
spectype, specsuffix = match
159
trace.mutter('Returning RevisionSpec %s for %s',
160
spectype.__name__, spec)
161
return spectype(spec, _internal=True)
163
for spectype in SPEC_TYPES:
164
if spec.startswith(spectype.prefix):
165
trace.mutter('Returning RevisionSpec %s for %s',
166
spectype.__name__, spec)
167
return spectype(spec, _internal=True)
171
168
# RevisionSpec_revno is special cased, because it is the only
172
169
# one that directly handles plain integers
173
170
# TODO: This should not be special cased rather it should be
202
198
def _match_on(self, branch, revs):
203
199
trace.mutter('Returning RevisionSpec._match_on: None')
204
return RevisionInfo(branch, 0, None)
200
return RevisionInfo(branch, None, None)
206
202
def _match_on_and_check(self, branch, revs):
207
203
info = self._match_on(branch, revs)
210
elif info == (0, None):
211
# special case - the empty tree
206
elif info == (None, None):
207
# special case - nothing supplied
213
209
elif self.prefix:
214
210
raise errors.InvalidRevisionSpec(self.user_spec, branch)
233
232
# will do what you expect.
234
233
in_store = in_history
235
234
in_branch = in_store
236
def as_revision_id(self, context_branch):
237
"""Return just the revision_id for this revisions spec.
239
Some revision specs require a context_branch to be able to determine
240
their value. Not all specs will make use of it.
242
return self._as_revision_id(context_branch)
244
def _as_revision_id(self, context_branch):
245
"""Implementation of as_revision_id()
247
Classes should override this function to provide appropriate
248
functionality. The default is to just call '.in_history().rev_id'
250
return self.in_history(context_branch).rev_id
252
def as_tree(self, context_branch):
253
"""Return the tree object for this revisions spec.
255
Some revision specs require a context_branch to be able to determine
256
the revision id and access the repository. Not all specs will make
259
return self._as_tree(context_branch)
261
def _as_tree(self, context_branch):
262
"""Implementation of as_tree().
264
Classes should override this function to provide appropriate
265
functionality. The default is to just call '.as_revision_id()'
266
and get the revision tree from context_branch's repository.
268
revision_id = self.as_revision_id(context_branch)
269
return context_branch.repository.revision_tree(revision_id)
237
271
def __repr__(self):
238
272
# this is mostly for helping with testing
239
273
return '<%s %s>' % (self.__class__.__name__,
242
276
def needs_branch(self):
243
277
"""Whether this revision spec needs a branch.
278
312
your history is very long.
280
314
prefix = 'revno:'
315
wants_revision_history = False
282
317
def _match_on(self, branch, revs):
283
318
"""Lookup a revision by revision number"""
319
branch, revno, revision_id = self._lookup(branch, revs)
320
return RevisionInfo(branch, revno, revision_id)
322
def _lookup(self, branch, revs_or_none):
284
323
loc = self.spec.find(':')
286
325
revno_spec = self.spec
315
354
# the branch object.
316
355
from bzrlib.branch import Branch
317
356
branch = Branch.open(branch_spec)
318
# Need to use a new revision history
319
# because we are using a specific branch
320
revs = branch.revision_history()
325
revision_id_to_revno = branch.get_revision_id_to_revno_map()
326
revisions = [revision_id for revision_id, revno
327
in revision_id_to_revno.iteritems()
328
if revno == match_revno]
331
if len(revisions) != 1:
332
return RevisionInfo(branch, None, None)
361
revision_id = branch.dotted_revno_to_revision_id(match_revno,
363
except errors.NoSuchRevision:
364
raise errors.InvalidRevisionSpec(self.user_spec, branch)
334
366
# there is no traditional 'revno' for dotted-decimal revnos.
335
367
# so for API compatability we return None.
336
return RevisionInfo(branch, None, revisions[0])
368
return branch, None, revision_id
370
last_revno, last_revision_id = branch.last_revision_info()
339
372
# if get_rev_id supported negative revnos, there would not be a
340
373
# need for this special case.
341
if (-revno) >= len(revs):
374
if (-revno) >= last_revno:
344
revno = len(revs) + revno + 1
377
revno = last_revno + revno + 1
346
revision_id = branch.get_rev_id(revno, revs)
379
revision_id = branch.get_rev_id(revno, revs_or_none)
347
380
except errors.NoSuchRevision:
348
381
raise errors.InvalidRevisionSpec(self.user_spec, branch)
349
return RevisionInfo(branch, revno, revision_id)
382
return branch, revno, revision_id
384
def _as_revision_id(self, context_branch):
385
# We would have the revno here, but we don't really care
386
branch, revno, revision_id = self._lookup(context_branch, None)
351
389
def needs_branch(self):
352
390
return self.spec.find(':') == -1
369
406
help_txt = """Selects a revision using the revision id.
371
408
Supply a specific revision id, that can be used to specify any
372
revision id in the ancestry of the branch.
409
revision id in the ancestry of the branch.
373
410
Including merges, and pending merges.
376
413
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
378
416
prefix = 'revid:'
380
418
def _match_on(self, branch, revs):
384
422
revision_id = osutils.safe_revision_id(self.spec, warn=False)
385
423
return RevisionInfo.from_revision_id(branch, revision_id, revs)
387
SPEC_TYPES.append(RevisionSpec_revid)
425
def _as_revision_id(self, context_branch):
426
return osutils.safe_revision_id(self.spec, warn=False)
390
430
class RevisionSpec_last(RevisionSpec):
399
439
last:1 -> return the last revision
400
440
last:3 -> return the revision 2 before the end.
405
445
def _match_on(self, branch, revs):
446
revno, revision_id = self._revno_and_revision_id(branch, revs)
447
return RevisionInfo(branch, revno, revision_id)
449
def _revno_and_revision_id(self, context_branch, revs_or_none):
450
last_revno, last_revision_id = context_branch.last_revision_info()
406
452
if self.spec == '':
408
raise errors.NoCommits(branch)
409
return RevisionInfo(branch, len(revs), revs[-1])
454
raise errors.NoCommits(context_branch)
455
return last_revno, last_revision_id
412
458
offset = int(self.spec)
413
459
except ValueError, e:
414
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
460
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
417
raise errors.InvalidRevisionSpec(self.user_spec, branch,
463
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
418
464
'you must supply a positive value')
419
revno = len(revs) - offset + 1
466
revno = last_revno - offset + 1
421
revision_id = branch.get_rev_id(revno, revs)
468
revision_id = context_branch.get_rev_id(revno, revs_or_none)
422
469
except errors.NoSuchRevision:
423
raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
return RevisionInfo(branch, revno, revision_id)
426
SPEC_TYPES.append(RevisionSpec_last)
470
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
471
return revno, revision_id
473
def _as_revision_id(self, context_branch):
474
# We compute the revno as part of the process, but we don't really care
476
revno, revision_id = self._revno_and_revision_id(context_branch, None)
429
481
class RevisionSpec_before(RevisionSpec):
432
484
help_txt = """Selects the parent of the revision specified.
434
Supply any revision spec to return the parent of that revision.
486
Supply any revision spec to return the parent of that revision. This is
487
mostly useful when inspecting revisions that are not in the revision history
435
490
It is an error to request the parent of the null revision (before:0).
436
This is mostly useful when inspecting revisions that are not in the
437
revision history of a branch.
441
494
before:1913 -> Return the parent of revno 1913 (revno 1912)
442
495
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
443
496
aaaa@bbbb-1234567890
444
bzr diff -r before:revid:aaaa..revid:aaaa
445
-> Find the changes between revision 'aaaa' and its parent.
446
(what changes did 'aaaa' introduce)
497
bzr diff -r before:1913..1913
498
-> Find the changes between revision 1913 and its parent (1912).
499
(What changes did revision 1913 introduce).
500
This is equivalent to: bzr diff -c 1913
449
503
prefix = 'before:'
451
505
def _match_on(self, branch, revs):
452
506
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
475
529
return RevisionInfo(branch, revno, revision_id)
477
SPEC_TYPES.append(RevisionSpec_before)
531
def _as_revision_id(self, context_branch):
532
base_revspec = RevisionSpec.from_string(self.spec)
533
base_revision_id = base_revspec.as_revision_id(context_branch)
534
if base_revision_id == revision.NULL_REVISION:
535
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
536
'cannot go before the null: revision')
537
context_repo = context_branch.repository
538
context_repo.lock_read()
540
parent_map = context_repo.get_parent_map([base_revision_id])
542
context_repo.unlock()
543
if base_revision_id not in parent_map:
544
# Ghost, or unknown revision id
545
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
546
'cannot find the matching revision')
547
parents = parent_map[base_revision_id]
549
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
550
'No parents for revision.')
480
555
class RevisionSpec_tag(RevisionSpec):
528
605
One way to display all the changes since yesterday would be::
530
bzr log -r date:yesterday..-1
607
bzr log -r date:yesterday..
534
611
date:yesterday -> select the first revision since yesterday
535
612
date:2006-08-14,17:10:14 -> select the first revision after
536
613
August 14th, 2006 at 5:10pm.
539
616
_date_re = re.compile(
540
617
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
629
705
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
630
706
return self._find_revision_info(branch, self.spec)
708
def _as_revision_id(self, context_branch):
709
return self._find_revision_id(context_branch, self.spec)
633
712
def _find_revision_info(branch, other_location):
713
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
716
revno = branch.revision_id_to_revno(revision_id)
717
except errors.NoSuchRevision:
719
return RevisionInfo(branch, revno, revision_id)
722
def _find_revision_id(branch, other_location):
634
723
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)
727
revision_a = revision.ensure_null(branch.last_revision())
728
if revision_a == revision.NULL_REVISION:
729
raise errors.NoCommits(branch)
730
if other_location == '':
731
other_location = branch.get_parent()
732
other_branch = Branch.open(other_location)
733
other_branch.lock_read()
735
revision_b = revision.ensure_null(other_branch.last_revision())
736
if revision_b == revision.NULL_REVISION:
737
raise errors.NoCommits(other_branch)
738
graph = branch.repository.get_graph(other_branch.repository)
739
rev_id = graph.find_unique_lca(revision_a, revision_b)
741
other_branch.unlock()
651
742
if rev_id == revision.NULL_REVISION:
652
743
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
SPEC_TYPES.append(RevisionSpec_ancestor)
663
751
class RevisionSpec_branch(RevisionSpec):
686
774
except errors.NoSuchRevision:
688
776
return RevisionInfo(branch, revno, revision_b)
690
SPEC_TYPES.append(RevisionSpec_branch)
778
def _as_revision_id(self, context_branch):
779
from bzrlib.branch import Branch
780
other_branch = Branch.open(self.spec)
781
last_revision = other_branch.last_revision()
782
last_revision = revision.ensure_null(last_revision)
783
context_branch.fetch(other_branch, last_revision)
784
if last_revision == revision.NULL_REVISION:
785
raise errors.NoCommits(other_branch)
788
def _as_tree(self, context_branch):
789
from bzrlib.branch import Branch
790
other_branch = Branch.open(self.spec)
791
last_revision = other_branch.last_revision()
792
last_revision = revision.ensure_null(last_revision)
793
if last_revision == revision.NULL_REVISION:
794
raise errors.NoCommits(other_branch)
795
return other_branch.repository.revision_tree(last_revision)
693
799
class RevisionSpec_submit(RevisionSpec_ancestor):
698
804
Diffing against this shows all the changes that were made in this branch,
699
805
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
806
used by the bundle and merge directive commands. If no submit branch
701
807
is specified, the parent branch is used instead.
703
809
The common ancestor is the last revision that existed in both
712
818
prefix = 'submit:'
714
def _match_on(self, branch, revs):
715
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
820
def _get_submit_location(self, branch):
716
821
submit_location = branch.get_submit_branch()
717
822
location_type = 'submit branch'
718
823
if submit_location is None:
721
826
if submit_location is None:
722
827
raise errors.NoSubmitBranch(branch)
723
828
trace.note('Using %s %s', location_type, submit_location)
724
return self._find_revision_info(branch, submit_location)
727
SPEC_TYPES.append(RevisionSpec_submit)
829
return submit_location
831
def _match_on(self, branch, revs):
832
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
833
return self._find_revision_info(branch,
834
self._get_submit_location(branch))
836
def _as_revision_id(self, context_branch):
837
return self._find_revision_id(context_branch,
838
self._get_submit_location(context_branch))
841
revspec_registry = registry.Registry()
842
def _register_revspec(revspec):
843
revspec_registry.register(revspec.prefix, revspec)
845
_register_revspec(RevisionSpec_revno)
846
_register_revspec(RevisionSpec_revid)
847
_register_revspec(RevisionSpec_last)
848
_register_revspec(RevisionSpec_before)
849
_register_revspec(RevisionSpec_tag)
850
_register_revspec(RevisionSpec_date)
851
_register_revspec(RevisionSpec_ancestor)
852
_register_revspec(RevisionSpec_branch)
853
_register_revspec(RevisionSpec_submit)
855
SPEC_TYPES = symbol_versioning.deprecated_list(
856
symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])