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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
22
from bzrlib import (
141
wants_revision_history = True
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)
144
149
def from_string(spec):
155
160
return RevisionSpec(None, _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)
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)
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)
168
171
# RevisionSpec_revno is special cased, because it is the only
169
172
# one that directly handles plain integers
170
173
# TODO: This should not be special cased rather it should be
198
202
def _match_on(self, branch, revs):
199
203
trace.mutter('Returning RevisionSpec._match_on: None')
200
return RevisionInfo(branch, None, None)
204
return RevisionInfo(branch, 0, None)
202
206
def _match_on_and_check(self, branch, revs):
203
207
info = self._match_on(branch, revs)
206
elif info == (None, None):
207
# special case - nothing supplied
210
elif info == (0, None):
211
# special case - the empty tree
209
213
elif self.prefix:
210
214
raise errors.InvalidRevisionSpec(self.user_spec, branch)
232
233
# will do what you expect.
233
234
in_store = in_history
234
235
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)
271
237
def __repr__(self):
272
238
# this is mostly for helping with testing
273
239
return '<%s %s>' % (self.__class__.__name__,
276
242
def needs_branch(self):
277
243
"""Whether this revision spec needs a branch.
312
278
your history is very long.
314
280
prefix = 'revno:'
315
wants_revision_history = False
317
282
def _match_on(self, branch, revs):
318
283
"""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):
323
284
loc = self.spec.find(':')
325
286
revno_spec = self.spec
354
315
# the branch object.
355
316
from bzrlib.branch import Branch
356
317
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()
361
revision_id = branch.dotted_revno_to_revision_id(match_revno,
363
except errors.NoSuchRevision:
364
raise errors.InvalidRevisionSpec(self.user_spec, branch)
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)
366
334
# there is no traditional 'revno' for dotted-decimal revnos.
367
335
# so for API compatability we return None.
368
return branch, None, revision_id
336
return RevisionInfo(branch, None, revisions[0])
370
last_revno, last_revision_id = branch.last_revision_info()
372
339
# if get_rev_id supported negative revnos, there would not be a
373
340
# need for this special case.
374
if (-revno) >= last_revno:
341
if (-revno) >= len(revs):
377
revno = last_revno + revno + 1
344
revno = len(revs) + revno + 1
379
revision_id = branch.get_rev_id(revno, revs_or_none)
346
revision_id = branch.get_rev_id(revno, revs)
380
347
except errors.NoSuchRevision:
381
348
raise errors.InvalidRevisionSpec(self.user_spec, branch)
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)
349
return RevisionInfo(branch, revno, revision_id)
389
351
def needs_branch(self):
390
352
return self.spec.find(':') == -1
406
369
help_txt = """Selects a revision using the revision id.
408
371
Supply a specific revision id, that can be used to specify any
409
revision id in the ancestry of the branch.
372
revision id in the ancestry of the branch.
410
373
Including merges, and pending merges.
413
376
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
416
378
prefix = 'revid:'
418
380
def _match_on(self, branch, revs):
422
384
revision_id = osutils.safe_revision_id(self.spec, warn=False)
423
385
return RevisionInfo.from_revision_id(branch, revision_id, revs)
425
def _as_revision_id(self, context_branch):
426
return osutils.safe_revision_id(self.spec, warn=False)
387
SPEC_TYPES.append(RevisionSpec_revid)
430
390
class RevisionSpec_last(RevisionSpec):
439
399
last:1 -> return the last revision
440
400
last:3 -> return the revision 2 before the end.
445
405
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()
452
406
if self.spec == '':
454
raise errors.NoCommits(context_branch)
455
return last_revno, last_revision_id
408
raise errors.NoCommits(branch)
409
return RevisionInfo(branch, len(revs), revs[-1])
458
412
offset = int(self.spec)
459
413
except ValueError, e:
460
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
414
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
463
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
417
raise errors.InvalidRevisionSpec(self.user_spec, branch,
464
418
'you must supply a positive value')
466
revno = last_revno - offset + 1
419
revno = len(revs) - offset + 1
468
revision_id = context_branch.get_rev_id(revno, revs_or_none)
421
revision_id = branch.get_rev_id(revno, revs)
469
422
except errors.NoSuchRevision:
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)
423
raise errors.InvalidRevisionSpec(self.user_spec, branch)
424
return RevisionInfo(branch, revno, revision_id)
426
SPEC_TYPES.append(RevisionSpec_last)
481
429
class RevisionSpec_before(RevisionSpec):
484
432
help_txt = """Selects the parent of the revision specified.
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
434
Supply any revision spec to return the parent of that revision.
490
435
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.
494
441
before:1913 -> Return the parent of revno 1913 (revno 1912)
495
442
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
496
443
aaaa@bbbb-1234567890
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
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)
503
449
prefix = 'before:'
505
451
def _match_on(self, branch, revs):
506
452
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
529
475
return RevisionInfo(branch, revno, revision_id)
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.')
477
SPEC_TYPES.append(RevisionSpec_before)
555
480
class RevisionSpec_tag(RevisionSpec):
605
528
One way to display all the changes since yesterday would be::
607
bzr log -r date:yesterday..
530
bzr log -r date:yesterday..-1
611
534
date:yesterday -> select the first revision since yesterday
612
535
date:2006-08-14,17:10:14 -> select the first revision after
613
536
August 14th, 2006 at 5:10pm.
616
539
_date_re = re.compile(
617
540
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
705
629
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
706
630
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)
712
633
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):
723
634
from bzrlib.branch import Branch
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()
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)
742
651
if rev_id == revision.NULL_REVISION:
743
652
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)
751
663
class RevisionSpec_branch(RevisionSpec):
774
686
except errors.NoSuchRevision:
776
688
return RevisionInfo(branch, revno, revision_b)
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)
690
SPEC_TYPES.append(RevisionSpec_branch)
799
693
class RevisionSpec_submit(RevisionSpec_ancestor):
804
698
Diffing against this shows all the changes that were made in this branch,
805
699
and is a good predictor of what merge will do. The submit branch is
806
used by the bundle and merge directive commands. If no submit branch
700
used by the bundle and merge directive comands. If no submit branch
807
701
is specified, the parent branch is used instead.
809
703
The common ancestor is the last revision that existed in both
818
712
prefix = 'submit:'
820
def _get_submit_location(self, branch):
714
def _match_on(self, branch, revs):
715
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
821
716
submit_location = branch.get_submit_branch()
822
717
location_type = 'submit branch'
823
718
if submit_location is None:
826
721
if submit_location is None:
827
722
raise errors.NoSubmitBranch(branch)
828
723
trace.note('Using %s %s', location_type, submit_location)
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", [])
724
return self._find_revision_info(branch, submit_location)
727
SPEC_TYPES.append(RevisionSpec_submit)