Merge lp:~fullermd/bzr/revspec-dwim into lp:bzr

Proposed by Matthew Fuller
Status: Superseded
Proposed branch: lp:~fullermd/bzr/revspec-dwim
Merge into: lp:bzr
Diff against target: 396 lines
6 files modified
NEWS (+4/-0)
bzrlib/help_topics/__init__.py (+10/-4)
bzrlib/option.py (+14/-18)
bzrlib/revisionspec.py (+80/-21)
bzrlib/tests/test_revisionspec.py (+43/-18)
doc/en/user-guide/specifying_revisions.txt (+10/-4)
To merge this branch: bzr merge lp:~fullermd/bzr/revspec-dwim
Reviewer Review Type Date Requested Status
Robert Collins (community) Needs Fixing
Ian Clatworthy Approve
Review via email: mp+13164@code.launchpad.net

This proposal has been superseded by a proposal from 2009-10-22.

To post a comment you must log in.
Revision history for this message
Matthew Fuller (fullermd) wrote :

This allows unqualified revspecs to be tried as revnos, tags, revids, dates, or branches (respectively) rather than being assumed to be revnos.

Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote :

I agree in principle with this and the code looks ok to me. There are 2 main concerns I have:

1. If something looks like a revno and isn't, I'd prefer that we raised an error in that case rather than continuing to look. The main issue here is tags like x.y.z I guess. I'd prefer we err'ed on the side of safety. It's not a big deal for projects to use a more explicit tag (like release-x.y.z) or for the user to type tag:x.y.z if they choose not to.

2. The time to look up a revision-id needs discussion. I'd prefer if it was done last. (If that's a performance issue, I'd prefer it was done immediately after revno but before the other specifiers.)

Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote :

After discussing this on IRC with a few people, I'm ok to proceed with this as is. Matthew has pinged the list just in case others have strong opinions. We ought to give that a day or two to see what feedback, if any, there is.

review: Approve
Revision history for this message
Robert Collins (lifeless) wrote :

 review needs-fixing

(however thats said now :()

This manually reproduces the registry of revspecs. Lets not do that,
instead query it dynamically.

If an ordering is needed, see the transport registry which has some
sorting done to it.

-Rob

review: Needs Fixing
Revision history for this message
Matthew Fuller (fullermd) wrote :

On Thu, Oct 15, 2009 at 05:33:12AM -0000 I heard the voice of
Robert Collins, and lo! it spake thus:
>
> This manually reproduces the registry of revspecs. Lets not do that,
> instead query it dynamically.
>
> If an ordering is needed, see the transport registry which has some
> sorting done to it.

I disagree.

Leaving aside the very large practical difficulties in doing it out of
the registry (and there are several), I consider it the wrong
approach. This isn't RevisionSpec_tryeverything, it's
RevisionSpec_dwim. An important part of a non-screwyou DWIM system is
carefully limiting to scope, and precisely defining the order within
that.

In this case, the dwim tries just half (rounded) of the revspecs we
have, because trying the rest makes no sense at all (e.g., if we don't
match it somewhere else, trying to match it to 'before:' would be
nonsensical, and in the bizarre case it actually DID something, it's
sure to be Wrong).

--
Matthew Fuller
<email address hidden>

Revision history for this message
Robert Collins (lifeless) wrote :

I disagree with your disagreement :)

specifically, git:, hg:, svn:, and thread: specs are added by plugins
and desirable to search too.

You can add this as a future enchancement if you like, but I think the
code will be smaller and more compact if you go straight to this.

-Rob

Revision history for this message
Matthew Fuller (fullermd) wrote :

> specifically, git:, hg:, svn:, and thread: specs are added by
> plugins and desirable to search too.

OK, so it sounds like there're actually two separate issues being a
little conflated here:

1) Make the DWIM use a registry instead of hand-written code.

   I'm not necessarily opposed to that; certainly IWBNI plugins could
   register their own specs DWIM should try. But I don't think I'll
   be doing it (certainly not anytime soon; $freetime--). And it's
   not quite as easy as it sounds, since we have to keep track of
   which exceptions each class raises for 'no, I didn't find this' (as
   opposed to 'crap, something went wrong').

   Also, instantiating the RevSpec_* classes _doesn't_ determine
   whether the given value points at anything, it only sets up the
   object to lazily search later. We reach inside them in the _dwim
   class to evaluate on the spot (when _dwim is lazily searched) to
   find out whether the value means anything to that spectype.

   So it's not just as simple as "do this then this then this";
   encoding that in a registry will take more knowledge of what you
   can do with python than I have. As it is, the body of the dwim
   _match_on() is about 40 lines (and with my characteristic heavy use
   of whitespace and commenting, so the actual code is probably under
   25), and explicit about the complications and what's going on.

2) Have that registry be the same as the revspec_registry registry
   that the prefix:'s are searched in.

   This I would be against, because 'prefixes we recognize' and
   'things we want to DWIM' are rather different, and cramming all of
   the above info, plus ordering, plus info on which specs we even
   DWIM at all, into that registry is a giant mess of making one thing
   do two almost-totally-different things. This is mostly what I was
   conceptually arguing against in the previous message, not so much
   (1) above.

--
Matthew Fuller
<email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2009-10-22 08:49:42 +0000
+++ NEWS 2009-10-22 19:16:16 +0000
@@ -233,6 +233,10 @@
233Improvements233Improvements
234************234************
235235
236* Revision specifiers can now be given in a more DWIM form, without
237 needing explicit prefixes for specifiers like tags or revision id's.
238 See ``bzr help revisionspec`` for full details. (Matthew Fuller)
239
236* Bazaar gives a warning before exiting, and writes into ``.bzr.log``, if 240* Bazaar gives a warning before exiting, and writes into ``.bzr.log``, if
237 compiled extensions can't be loaded. This typically indicates a241 compiled extensions can't be loaded. This typically indicates a
238 packaging or installation problem. In this case Bazaar will keep242 packaging or installation problem. In this case Bazaar will keep
239243
=== modified file 'bzrlib/help_topics/__init__.py'
--- bzrlib/help_topics/__init__.py 2009-10-13 20:15:45 +0000
+++ bzrlib/help_topics/__init__.py 2009-10-22 19:16:16 +0000
@@ -152,10 +152,16 @@
152 out.append(152 out.append(
153"""Revision Identifiers153"""Revision Identifiers
154154
155A revision identifier refers to a specific state of a branch's history. It can155A revision identifier refers to a specific state of a branch's history. It
156be a revision number, or a keyword followed by ':' and often other156can be expressed in several ways. It can begin with a keyword to
157parameters. Some examples of identifiers are '3', 'last:1', 'before:yesterday'157unambiguously specify a given lookup type; some examples are 'last:1',
158and 'submit:'.158'before:yesterday' and 'submit:'.
159
160Alternately, it can be given without a keyword, in which case it will be
161checked as a revision number, a tag, a revision id, a date specification, or a
162branch specification, in that order. For example, 'date:today' could be
163written as simply 'today', though if you have a tag called 'today' that will
164be found first.
159165
160If 'REV1' and 'REV2' are revision identifiers, then 'REV1..REV2' denotes a166If 'REV1' and 'REV2' are revision identifiers, then 'REV1..REV2' denotes a
161revision range. Examples: '3647..3649', 'date:yesterday..-1' and167revision range. Examples: '3647..3649', 'date:yesterday..-1' and
162168
=== modified file 'bzrlib/option.py'
--- bzrlib/option.py 2009-04-03 20:05:25 +0000
+++ bzrlib/option.py 2009-10-22 19:16:16 +0000
@@ -40,25 +40,25 @@
40 each revision specifier supplied.40 each revision specifier supplied.
4141
42 >>> _parse_revision_str('234')42 >>> _parse_revision_str('234')
43 [<RevisionSpec_revno 234>]43 [<RevisionSpec_dwim 234>]
44 >>> _parse_revision_str('234..567')44 >>> _parse_revision_str('234..567')
45 [<RevisionSpec_revno 234>, <RevisionSpec_revno 567>]45 [<RevisionSpec_dwim 234>, <RevisionSpec_dwim 567>]
46 >>> _parse_revision_str('..')46 >>> _parse_revision_str('..')
47 [<RevisionSpec None>, <RevisionSpec None>]47 [<RevisionSpec None>, <RevisionSpec None>]
48 >>> _parse_revision_str('..234')48 >>> _parse_revision_str('..234')
49 [<RevisionSpec None>, <RevisionSpec_revno 234>]49 [<RevisionSpec None>, <RevisionSpec_dwim 234>]
50 >>> _parse_revision_str('234..')50 >>> _parse_revision_str('234..')
51 [<RevisionSpec_revno 234>, <RevisionSpec None>]51 [<RevisionSpec_dwim 234>, <RevisionSpec None>]
52 >>> _parse_revision_str('234..456..789') # Maybe this should be an error52 >>> _parse_revision_str('234..456..789') # Maybe this should be an error
53 [<RevisionSpec_revno 234>, <RevisionSpec_revno 456>, <RevisionSpec_revno 789>]53 [<RevisionSpec_dwim 234>, <RevisionSpec_dwim 456>, <RevisionSpec_dwim 789>]
54 >>> _parse_revision_str('234....789') #Error ?54 >>> _parse_revision_str('234....789') #Error ?
55 [<RevisionSpec_revno 234>, <RevisionSpec None>, <RevisionSpec_revno 789>]55 [<RevisionSpec_dwim 234>, <RevisionSpec None>, <RevisionSpec_dwim 789>]
56 >>> _parse_revision_str('revid:test@other.com-234234')56 >>> _parse_revision_str('revid:test@other.com-234234')
57 [<RevisionSpec_revid revid:test@other.com-234234>]57 [<RevisionSpec_revid revid:test@other.com-234234>]
58 >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')58 >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
59 [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]59 [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
60 >>> _parse_revision_str('revid:test@other.com-234234..23')60 >>> _parse_revision_str('revid:test@other.com-234234..23')
61 [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revno 23>]61 [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_dwim 23>]
62 >>> _parse_revision_str('date:2005-04-12')62 >>> _parse_revision_str('date:2005-04-12')
63 [<RevisionSpec_date date:2005-04-12>]63 [<RevisionSpec_date date:2005-04-12>]
64 >>> _parse_revision_str('date:2005-04-12 12:24:33')64 >>> _parse_revision_str('date:2005-04-12 12:24:33')
@@ -68,27 +68,23 @@
68 >>> _parse_revision_str('date:2005-04-12,12:24:33')68 >>> _parse_revision_str('date:2005-04-12,12:24:33')
69 [<RevisionSpec_date date:2005-04-12,12:24:33>]69 [<RevisionSpec_date date:2005-04-12,12:24:33>]
70 >>> _parse_revision_str('-5..23')70 >>> _parse_revision_str('-5..23')
71 [<RevisionSpec_revno -5>, <RevisionSpec_revno 23>]71 [<RevisionSpec_dwim -5>, <RevisionSpec_dwim 23>]
72 >>> _parse_revision_str('-5')72 >>> _parse_revision_str('-5')
73 [<RevisionSpec_revno -5>]73 [<RevisionSpec_dwim -5>]
74 >>> _parse_revision_str('123a')74 >>> _parse_revision_str('123a')
75 Traceback (most recent call last):75 [<RevisionSpec_dwim 123a>]
76 ...
77 NoSuchRevisionSpec: No namespace registered for string: '123a'
78 >>> _parse_revision_str('abc')76 >>> _parse_revision_str('abc')
79 Traceback (most recent call last):77 [<RevisionSpec_dwim abc>]
80 ...
81 NoSuchRevisionSpec: No namespace registered for string: 'abc'
82 >>> _parse_revision_str('branch:../branch2')78 >>> _parse_revision_str('branch:../branch2')
83 [<RevisionSpec_branch branch:../branch2>]79 [<RevisionSpec_branch branch:../branch2>]
84 >>> _parse_revision_str('branch:../../branch2')80 >>> _parse_revision_str('branch:../../branch2')
85 [<RevisionSpec_branch branch:../../branch2>]81 [<RevisionSpec_branch branch:../../branch2>]
86 >>> _parse_revision_str('branch:../../branch2..23')82 >>> _parse_revision_str('branch:../../branch2..23')
87 [<RevisionSpec_branch branch:../../branch2>, <RevisionSpec_revno 23>]83 [<RevisionSpec_branch branch:../../branch2>, <RevisionSpec_dwim 23>]
88 >>> _parse_revision_str('branch:..\\\\branch2')84 >>> _parse_revision_str('branch:..\\\\branch2')
89 [<RevisionSpec_branch branch:..\\branch2>]85 [<RevisionSpec_branch branch:..\\branch2>]
90 >>> _parse_revision_str('branch:..\\\\..\\\\branch2..23')86 >>> _parse_revision_str('branch:..\\\\..\\\\branch2..23')
91 [<RevisionSpec_branch branch:..\\..\\branch2>, <RevisionSpec_revno 23>]87 [<RevisionSpec_branch branch:..\\..\\branch2>, <RevisionSpec_dwim 23>]
92 """88 """
93 # TODO: Maybe move this into revisionspec.py89 # TODO: Maybe move this into revisionspec.py
94 revs = []90 revs = []
@@ -104,7 +100,7 @@
104 parent of the revision.100 parent of the revision.
105101
106 >>> _parse_change_str('123')102 >>> _parse_change_str('123')
107 (<RevisionSpec_before before:123>, <RevisionSpec_revno 123>)103 (<RevisionSpec_before before:123>, <RevisionSpec_dwim 123>)
108 >>> _parse_change_str('123..124')104 >>> _parse_change_str('123..124')
109 Traceback (most recent call last):105 Traceback (most recent call last):
110 ...106 ...
111107
=== modified file 'bzrlib/revisionspec.py'
--- bzrlib/revisionspec.py 2009-07-25 08:26:42 +0000
+++ bzrlib/revisionspec.py 2009-10-22 19:16:16 +0000
@@ -113,8 +113,6 @@
113 return RevisionInfo(branch, revno, revision_id)113 return RevisionInfo(branch, revno, revision_id)
114114
115115
116# classes in this list should have a "prefix" attribute, against which
117# string specs are matched
118_revno_regex = None116_revno_regex = None
119117
120118
@@ -123,10 +121,10 @@
123121
124 help_txt = """A parsed revision specification.122 help_txt = """A parsed revision specification.
125123
126 A revision specification can be an integer, in which case it is124 A revision specification is a string, which may be unambiguous about
127 assumed to be a revno (though this will translate negative values125 what it represents by giving a prefix like 'date:' or 'revid:' etc,
128 into positive ones); or it can be a string, in which case it is126 or it may have no prefix, in which case it's tried against several
129 parsed for something like 'date:' or 'revid:' etc.127 specifier types in sequence to determine what the user meant.
130128
131 Revision specs are an UI element, and they have been moved out129 Revision specs are an UI element, and they have been moved out
132 of the branch class to leave "back-end" classes unaware of such130 of the branch class to leave "back-end" classes unaware of such
@@ -139,6 +137,14 @@
139137
140 prefix = None138 prefix = None
141 wants_revision_history = True139 wants_revision_history = True
140 dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
141 """Expections that RevisionSpec_dwim._match_on will catch.
142
143 If the revspec is part of dwim_revspecs, it may be tried with an invalid
144 revspec and raise some exception. The exceptions mentioned here will not be
145 reported to the user but simply ignored without stopping the dwim
146 processing.
147 """
142148
143 @staticmethod149 @staticmethod
144 def from_string(spec):150 def from_string(spec):
@@ -165,17 +171,9 @@
165 trace.mutter('Returning RevisionSpec %s for %s',171 trace.mutter('Returning RevisionSpec %s for %s',
166 spectype.__name__, spec)172 spectype.__name__, spec)
167 return spectype(spec, _internal=True)173 return spectype(spec, _internal=True)
168 # RevisionSpec_revno is special cased, because it is the only174 # Otherwise treat it as a DWIM, build the RevisionSpec object and
169 # one that directly handles plain integers175 # wait for _match_on to be called.
170 # TODO: This should not be special cased rather it should be176 return RevisionSpec_dwim(spec, _internal=True)
171 # a method invocation on spectype.canparse()
172 global _revno_regex
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)
177
178 raise errors.NoSuchRevisionSpec(spec)
179177
180 def __init__(self, spec, _internal=False):178 def __init__(self, spec, _internal=False):
181 """Create a RevisionSpec referring to the Null revision.179 """Create a RevisionSpec referring to the Null revision.
@@ -290,16 +288,62 @@
290288
291# private API289# private API
292290
291class RevisionSpec_dwim(RevisionSpec):
292 """Provides a DWIMish revision specifier lookup.
293
294 Note that this does not go in the revspec_registry because by definition
295 there is no prefix to identify it. It's solely called from
296 RevisionSpec.from_string() because the DWIMification happen when _match_on
297 is called so the string describing the revision is kept here until needed.
298 """
299
300 help_txt = None
301 # We don't need to build the revision history ourself, that's delegated to
302 # each revspec we try.
303 wants_revision_history = False
304
305 def _try_spectype(self, rstype, branch):
306 rs = rstype(self.spec, _internal=True)
307 # Hit in_history to find out if it exists, or we need to try the
308 # next type.
309 return rs.in_history(branch)
310
311 def _match_on(self, branch, revs):
312 """Run the lookup and see what we can get."""
313
314 # First, see if it's a revno
315 global _revno_regex
316 if _revno_regex is None:
317 _revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
318 if _revno_regex.match(self.spec) is not None:
319 try:
320 return self._try_spectype(RevisionSpec_revno, branch)
321 except RevisionSpec_revno.dwim_catchable_exceptions:
322 pass
323
324 # Next see what has been registered
325 for rs_class in dwim_revspecs:
326 try:
327 return self._try_spectype(rs_class, branch)
328 except rs_class.dwim_catchable_exceptions:
329 pass
330
331 # Well, I dunno what it is. Note that we don't try to keep track of the
332 # first of last exception raised during the DWIM tries as none seems
333 # really relevant.
334 raise errors.InvalidRevisionSpec(self.spec, branch)
335
336
293class RevisionSpec_revno(RevisionSpec):337class RevisionSpec_revno(RevisionSpec):
294 """Selects a revision using a number."""338 """Selects a revision using a number."""
295339
296 help_txt = """Selects a revision using a number.340 help_txt = """Selects a revision using a number.
297341
298 Use an integer to specify a revision in the history of the branch.342 Use an integer to specify a revision in the history of the branch.
299 Optionally a branch can be specified. The 'revno:' prefix is optional.343 Optionally a branch can be specified. A negative number will count
300 A negative number will count from the end of the branch (-1 is the344 from the end of the branch (-1 is the last revision, -2 the previous
301 last revision, -2 the previous one). If the negative number is larger345 one). If the negative number is larger than the branch's history, the
302 than the branch's history, the first revision is returned.346 first revision is returned.
303 Examples::347 Examples::
304348
305 revno:1 -> return the first revision of this branch349 revno:1 -> return the first revision of this branch
@@ -561,6 +605,7 @@
561 """605 """
562606
563 prefix = 'tag:'607 prefix = 'tag:'
608 dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
564609
565 def _match_on(self, branch, revs):610 def _match_on(self, branch, revs):
566 # Can raise tags not supported, NoSuchTag, etc611 # Can raise tags not supported, NoSuchTag, etc
@@ -760,6 +805,7 @@
760 branch:/path/to/branch805 branch:/path/to/branch
761 """806 """
762 prefix = 'branch:'807 prefix = 'branch:'
808 dwim_catchable_exceptions = (errors.NotBranchError,)
763809
764 def _match_on(self, branch, revs):810 def _match_on(self, branch, revs):
765 from bzrlib.branch import Branch811 from bzrlib.branch import Branch
@@ -838,6 +884,17 @@
838 self._get_submit_location(context_branch))884 self._get_submit_location(context_branch))
839885
840886
887# The order in which we want to DWIM a revision spec without any prefix.
888# revno is always tried first and isn't listed here, this is used by
889# RevisionSpec_dwim._match_on
890dwim_revspecs = [
891 RevisionSpec_tag, # Let's try for a tag
892 RevisionSpec_revid, # Maybe it's a revid?
893 RevisionSpec_date, # Perhaps a date?
894 RevisionSpec_branch, # OK, last try, maybe it's a branch
895 ]
896
897
841revspec_registry = registry.Registry()898revspec_registry = registry.Registry()
842def _register_revspec(revspec):899def _register_revspec(revspec):
843 revspec_registry.register(revspec.prefix, revspec)900 revspec_registry.register(revspec.prefix, revspec)
@@ -852,5 +909,7 @@
852_register_revspec(RevisionSpec_branch)909_register_revspec(RevisionSpec_branch)
853_register_revspec(RevisionSpec_submit)910_register_revspec(RevisionSpec_submit)
854911
912# classes in this list should have a "prefix" attribute, against which
913# string specs are matched
855SPEC_TYPES = symbol_versioning.deprecated_list(914SPEC_TYPES = symbol_versioning.deprecated_list(
856 symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])915 symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])
857916
=== modified file 'bzrlib/tests/test_revisionspec.py'
--- bzrlib/tests/test_revisionspec.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/test_revisionspec.py 2009-10-22 19:16:16 +0000
@@ -146,24 +146,49 @@
146 def test_object(self):146 def test_object(self):
147 self.assertRaises(TypeError, RevisionSpec.from_string, object())147 self.assertRaises(TypeError, RevisionSpec.from_string, object())
148148
149 def test_unregistered_spec(self):149
150 self.assertRaises(errors.NoSuchRevisionSpec,150class TestRevisionSpec_dwim(TestRevisionSpec):
151 RevisionSpec.from_string, 'foo')151
152 self.assertRaises(errors.NoSuchRevisionSpec,152 # Don't need to test revno's explicitly since TRS_revno already
153 RevisionSpec.from_string, '123a')153 # covers that well for us
154154 def test_dwim_spec_revno(self):
155155 self.assertInHistoryIs(2, 'r2', '2')
156156 self.assertAsRevisionId('alt_r2', '1.1.1')
157class TestRevnoFromString(TestCase):157
158158 def test_dwim_spec_revid(self):
159 def test_from_string_dotted_decimal(self):159 self.assertInHistoryIs(2, 'r2', 'r2')
160 self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '-1.1')160
161 self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '.1')161 def test_dwim_spec_tag(self):
162 self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1..1')162 self.tree.branch.tags.set_tag('footag', 'r1')
163 self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.2..1')163 self.assertAsRevisionId('r1', 'footag')
164 self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.')164 self.tree.branch.tags.delete_tag('footag')
165 self.assertIsInstance(RevisionSpec.from_string('1.1'), RevisionSpec_revno)165 self.assertRaises(errors.InvalidRevisionSpec,
166 self.assertIsInstance(RevisionSpec.from_string('1.1.3'), RevisionSpec_revno)166 self.get_in_history, 'footag')
167
168 def test_dwim_spec_tag_that_looks_like_revno(self):
169 # Test that we slip past revno with things that look like revnos,
170 # but aren't. Tags are convenient for testing this since we can
171 # make them look however we want.
172 self.tree.branch.tags.set_tag('3', 'r2')
173 self.assertAsRevisionId('r2', '3')
174 self.build_tree(['tree/b'])
175 self.tree.add(['b'])
176 self.tree.commit('b', rev_id='r3')
177 self.assertAsRevisionId('r3', '3')
178
179 def test_dwim_spec_date(self):
180 self.assertAsRevisionId('r1', 'today')
181
182 def test_dwim_spec_branch(self):
183 self.assertInHistoryIs(None, 'alt_r2', 'tree2')
184
185 def test_dwim_spec_nonexistent(self):
186 self.assertInvalid('somethingrandom', invalid_as_revision_id=False)
187 self.assertInvalid('-1.1', invalid_as_revision_id=False)
188 self.assertInvalid('.1', invalid_as_revision_id=False)
189 self.assertInvalid('1..1', invalid_as_revision_id=False)
190 self.assertInvalid('1.2..1', invalid_as_revision_id=False)
191 self.assertInvalid('1.', invalid_as_revision_id=False)
167192
168193
169class TestRevisionSpec_revno(TestRevisionSpec):194class TestRevisionSpec_revno(TestRevisionSpec):
170195
=== modified file 'doc/en/user-guide/specifying_revisions.txt'
--- doc/en/user-guide/specifying_revisions.txt 2009-04-04 02:57:47 +0000
+++ doc/en/user-guide/specifying_revisions.txt 2009-10-22 19:16:16 +0000
@@ -36,17 +36,23 @@
36 +----------------------+------------------------------------+36 +----------------------+------------------------------------+
37 | *number* | revision number |37 | *number* | revision number |
38 +----------------------+------------------------------------+38 +----------------------+------------------------------------+
39 | **revno**:*number* | positive revision number |39 | **revno**:*number* | revision number |
40 +----------------------+------------------------------------+40 +----------------------+------------------------------------+
41 | **last**:*number* | negative revision number |41 | **last**:*number* | negative revision number |
42 +----------------------+------------------------------------+42 +----------------------+------------------------------------+
43 | *guid* | globally unique revision id |
44 +----------------------+------------------------------------+
43 | **revid**:*guid* | globally unique revision id |45 | **revid**:*guid* | globally unique revision id |
44 +----------------------+------------------------------------+46 +----------------------+------------------------------------+
45 | **before**:*rev* | leftmost parent of ''rev'' |47 | **before**:*rev* | leftmost parent of ''rev'' |
46 +----------------------+------------------------------------+48 +----------------------+------------------------------------+
47 | **date**:*value* | first entry after a given date |49 | *date-value* | first entry after a given date |
48 +----------------------+------------------------------------+50 +----------------------+------------------------------------+
49 | **tag**:*value* | revision matching a given tag |51 | **date**:*date-value*| first entry after a given date |
52 +----------------------+------------------------------------+
53 | *tag-name* | revision matching a given tag |
54 +----------------------+------------------------------------+
55 | **tag**:*tag-name* | revision matching a given tag |
50 +----------------------+------------------------------------+56 +----------------------+------------------------------------+
51 | **ancestor**:*path* | last merged revision from a branch |57 | **ancestor**:*path* | last merged revision from a branch |
52 +----------------------+------------------------------------+58 +----------------------+------------------------------------+