124
124
help_txt = """A parsed revision specification.
126
A revision specification is a string, which may be unambiguous about
127
what it represents by giving a prefix like 'date:' or 'revid:' etc,
128
or it may have no prefix, in which case it's tried against several
129
specifier types in sequence to determine what the user meant.
126
A revision specification can be an integer, in which case it is
127
assumed to be a revno (though this will translate negative values
128
into positive ones); or it can be a string, in which case it is
129
parsed for something like 'date:' or 'revid:' etc.
131
131
Revision specs are an UI element, and they have been moved out
132
132
of the branch class to leave "back-end" classes unaware of such
169
161
return spectype(spec, _internal=True)
171
163
for spectype in SPEC_TYPES:
164
trace.mutter('Returning RevisionSpec %s for %s',
165
spectype.__name__, spec)
172
166
if spec.startswith(spectype.prefix):
173
trace.mutter('Returning RevisionSpec %s for %s',
174
spectype.__name__, spec)
175
167
return spectype(spec, _internal=True)
176
# Otherwise treat it as a DWIM, build the RevisionSpec object and
177
# wait for _match_on to be called.
178
return RevisionSpec_dwim(spec, _internal=True)
168
# RevisionSpec_revno is special cased, because it is the only
169
# one that directly handles plain integers
170
# TODO: This should not be special cased rather it should be
171
# a method invocation on spectype.canparse()
173
if _revno_regex is None:
174
_revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
175
if _revno_regex.match(spec) is not None:
176
return RevisionSpec_revno(spec, _internal=True)
178
raise errors.NoSuchRevisionSpec(spec)
180
180
def __init__(self, spec, _internal=False):
181
181
"""Create a RevisionSpec referring to the Null revision.
293
class RevisionSpec_dwim(RevisionSpec):
294
"""Provides a DWIMish revision specifier lookup.
296
Note that this does not go in the revspec_registry because by definition
297
there is no prefix to identify it. It's solely called from
298
RevisionSpec.from_string() because the DWIMification happen when _match_on
299
is called so the string describing the revision is kept here until needed.
303
# We don't need to build the revision history ourself, that's delegated to
304
# each revspec we try.
305
wants_revision_history = False
307
def _try_spectype(self, rstype, branch):
308
rs = rstype(self.spec, _internal=True)
309
# Hit in_history to find out if it exists, or we need to try the
311
return rs.in_history(branch)
313
def _match_on(self, branch, revs):
314
"""Run the lookup and see what we can get."""
316
# First, see if it's a revno
318
if _revno_regex is None:
319
_revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
320
if _revno_regex.match(self.spec) is not None:
322
return self._try_spectype(RevisionSpec_revno, branch)
323
except RevisionSpec_revno.dwim_catchable_exceptions:
326
# Next see what has been registered
327
for rs_class in dwim_revspecs:
329
return self._try_spectype(rs_class, branch)
330
except rs_class.dwim_catchable_exceptions:
333
# Well, I dunno what it is. Note that we don't try to keep track of the
334
# first of last exception raised during the DWIM tries as none seems
336
raise errors.InvalidRevisionSpec(self.spec, branch)
339
294
class RevisionSpec_revno(RevisionSpec):
340
295
"""Selects a revision using a number."""
342
297
help_txt = """Selects a revision using a number.
344
299
Use an integer to specify a revision in the history of the branch.
345
Optionally a branch can be specified. A negative number will count
346
from the end of the branch (-1 is the last revision, -2 the previous
347
one). If the negative number is larger than the branch's history, the
348
first revision is returned.
300
Optionally a branch can be specified. The 'revno:' prefix is optional.
301
A negative number will count from the end of the branch (-1 is the
302
last revision, -2 the previous one). If the negative number is larger
303
than the branch's history, the first revision is returned.
351
306
revno:1 -> return the first revision of this branch
449
class RevisionIDSpec(RevisionSpec):
451
def _match_on(self, branch, revs):
452
revision_id = self.as_revision_id(branch)
453
return RevisionInfo.from_revision_id(branch, revision_id, revs)
456
class RevisionSpec_revid(RevisionIDSpec):
404
class RevisionSpec_revid(RevisionSpec):
457
405
"""Selects a revision using the revision id."""
459
407
help_txt = """Selects a revision using the revision id.
469
417
prefix = 'revid:'
471
def _as_revision_id(self, context_branch):
419
def _match_on(self, branch, revs):
472
420
# self.spec comes straight from parsing the command line arguments,
473
421
# so we expect it to be a Unicode string. Switch it to the internal
474
422
# representation.
423
revision_id = osutils.safe_revision_id(self.spec, warn=False)
424
return RevisionInfo.from_revision_id(branch, revision_id, revs)
426
def _as_revision_id(self, context_branch):
475
427
return osutils.safe_revision_id(self.spec, warn=False)
818
768
revision_b = other_branch.last_revision()
819
769
if revision_b in (None, revision.NULL_REVISION):
820
770
raise errors.NoCommits(other_branch)
822
branch = other_branch
825
# pull in the remote revisions so we can diff
826
branch.fetch(other_branch, revision_b)
827
except errors.ReadOnlyError:
828
branch = other_branch
771
# pull in the remote revisions so we can diff
772
branch.fetch(other_branch, revision_b)
830
774
revno = branch.revision_id_to_revno(revision_b)
831
775
except errors.NoSuchRevision:
901
839
self._get_submit_location(context_branch))
904
class RevisionSpec_annotate(RevisionIDSpec):
908
help_txt = """Select the revision that last modified the specified line.
910
Select the revision that last modified the specified line. Line is
911
specified as path:number. Path is a relative path to the file. Numbers
912
start at 1, and are relative to the current version, not the last-
913
committed version of the file.
916
def _raise_invalid(self, numstring, context_branch):
917
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
918
'No such line: %s' % numstring)
920
def _as_revision_id(self, context_branch):
921
path, numstring = self.spec.rsplit(':', 1)
923
index = int(numstring) - 1
925
self._raise_invalid(numstring, context_branch)
926
tree, file_path = workingtree.WorkingTree.open_containing(path)
929
file_id = tree.path2id(file_path)
931
raise errors.InvalidRevisionSpec(self.user_spec,
932
context_branch, "File '%s' is not versioned." %
934
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
938
revision_id = revision_ids[index]
940
self._raise_invalid(numstring, context_branch)
941
if revision_id == revision.CURRENT_REVISION:
942
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
943
'Line %s has not been committed.' % numstring)
947
class RevisionSpec_mainline(RevisionIDSpec):
949
help_txt = """Select mainline revision that merged the specified revision.
951
Select the revision that merged the specified revision into mainline.
956
def _as_revision_id(self, context_branch):
957
revspec = RevisionSpec.from_string(self.spec)
958
if revspec.get_branch() is None:
959
spec_branch = context_branch
961
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
962
revision_id = revspec.as_revision_id(spec_branch)
963
graph = context_branch.repository.get_graph()
964
result = graph.find_lefthand_merger(revision_id,
965
context_branch.last_revision())
967
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
971
# The order in which we want to DWIM a revision spec without any prefix.
972
# revno is always tried first and isn't listed here, this is used by
973
# RevisionSpec_dwim._match_on
975
RevisionSpec_tag, # Let's try for a tag
976
RevisionSpec_revid, # Maybe it's a revid?
977
RevisionSpec_date, # Perhaps a date?
978
RevisionSpec_branch, # OK, last try, maybe it's a branch
982
842
revspec_registry = registry.Registry()
983
843
def _register_revspec(revspec):
984
844
revspec_registry.register(revspec.prefix, revspec)
992
852
_register_revspec(RevisionSpec_ancestor)
993
853
_register_revspec(RevisionSpec_branch)
994
854
_register_revspec(RevisionSpec_submit)
995
_register_revspec(RevisionSpec_annotate)
996
_register_revspec(RevisionSpec_mainline)
998
# classes in this list should have a "prefix" attribute, against which
999
# string specs are matched
1000
856
SPEC_TYPES = symbol_versioning.deprecated_list(
1001
857
symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])