33
34
class RevisionInfo(object):
34
"""The results of applying a revision specification to a branch.
35
"""The results of applying a revision specification to a branch."""
37
help_txt = """The results of applying a revision specification to a branch.
36
39
An instance has two useful attributes: revno, and rev_id.
100
103
class RevisionSpec(object):
101
"""A parsed revision specification.
104
"""A parsed revision specification."""
106
help_txt = """A parsed revision specification.
103
108
A revision specification can be an integer, in which case it is
104
109
assumed to be a revno (though this will translate negative values
153
158
# RevisionSpec_revno is special cased, because it is the only
154
159
# one that directly handles plain integers
160
# TODO: This should not be special cased rather it should be
161
# a method invocation on spectype.canparse()
155
162
global _revno_regex
156
163
if _revno_regex is None:
157
_revno_regex = re.compile(r'-?\d+(:.*)?$')
164
_revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
158
165
if _revno_regex.match(spec) is not None:
159
166
return RevisionSpec_revno(spec, _internal=True)
237
246
class RevisionSpec_revno(RevisionSpec):
247
"""Selects a revision using a number."""
249
help_txt = """Selects a revision using a number.
251
Use an integer to specify a revision in the history of the branch.
252
Optionally a branch can be specified. The 'revno:' prefix is optional.
253
A negative number will count from the end of the branch (-1 is the
254
last revision, -2 the previous one). If the negative number is larger
255
than the branch's history, the first revision is returned.
257
revno:1 -> return the first revision
258
revno:3:/path/to/branch -> return the 3rd revision of
259
the branch '/path/to/branch'
260
revno:-1 -> The last revision in a branch.
261
-2:http://other/branch -> The second to last revision in the
263
-1000000 -> Most likely the first revision, unless
264
your history is very long.
238
266
prefix = 'revno:'
240
268
def _match_on(self, branch, revs):
257
285
revno = int(revno_spec)
258
except ValueError, e:
259
raise errors.InvalidRevisionSpec(self.user_spec,
288
# dotted decimal. This arguably should not be here
289
# but the from_string method is a little primitive
290
# right now - RBC 20060928
292
match_revno = tuple((int(number) for number in revno_spec.split('.')))
293
except ValueError, e:
294
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
299
# the user has override the branch to look in.
300
# we need to refresh the revision_history map and
263
302
from bzrlib.branch import Branch
264
303
branch = Branch.open(branch_spec)
265
304
# Need to use a new revision history
266
305
# because we are using a specific branch
267
306
revs = branch.revision_history()
270
if (-revno) >= len(revs):
311
last_rev = branch.last_revision()
312
merge_sorted_revisions = tsort.merge_sort(
313
branch.repository.get_revision_graph(last_rev),
317
return item[3] == match_revno
318
revisions = filter(match, merge_sorted_revisions)
321
if len(revisions) != 1:
322
return RevisionInfo(branch, None, None)
273
revno = len(revs) + revno + 1
275
revision_id = branch.get_rev_id(revno, revs)
276
except errors.NoSuchRevision:
277
raise errors.InvalidRevisionSpec(self.user_spec, branch)
324
# there is no traditional 'revno' for dotted-decimal revnos.
325
# so for API compatability we return None.
326
return RevisionInfo(branch, None, revisions[0][1])
329
# if get_rev_id supported negative revnos, there would not be a
330
# need for this special case.
331
if (-revno) >= len(revs):
334
revno = len(revs) + revno + 1
336
revision_id = branch.get_rev_id(revno, revs)
337
except errors.NoSuchRevision:
338
raise errors.InvalidRevisionSpec(self.user_spec, branch)
278
339
return RevisionInfo(branch, revno, revision_id)
280
341
def needs_branch(self):
295
356
class RevisionSpec_revid(RevisionSpec):
357
"""Selects a revision using the revision id."""
359
help_txt = """Selects a revision using the revision id.
361
Supply a specific revision id, that can be used to specify any
362
revision id in the ancestry of the branch.
363
Including merges, and pending merges.
365
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
296
367
prefix = 'revid:'
298
369
def _match_on(self, branch, revs):
308
379
class RevisionSpec_last(RevisionSpec):
380
"""Selects the nth revision from the end."""
382
help_txt = """Selects the nth revision from the end.
384
Supply a positive number to get the nth revision from the end.
385
This is the same as supplying negative numbers to the 'revno:' spec.
387
last:1 -> return the last revision
388
last:3 -> return the revision 2 before the end.
336
417
class RevisionSpec_before(RevisionSpec):
418
"""Selects the parent of the revision specified."""
420
help_txt = """Selects the parent of the revision specified.
422
Supply any revision spec to return the parent of that revision.
423
It is an error to request the parent of the null revision (before:0).
424
This is mostly useful when inspecting revisions that are not in the
425
revision history of a branch.
428
before:1913 -> Return the parent of revno 1913 (revno 1912)
429
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
431
bzr diff -r before:revid:aaaa..revid:aaaa
432
-> Find the changes between revision 'aaaa' and its parent.
433
(what changes did 'aaaa' introduce)
338
436
prefix = 'before:'
399
501
class RevisionSpec_date(RevisionSpec):
502
"""Selects a revision on the basis of a datestamp."""
504
help_txt = """Selects a revision on the basis of a datestamp.
506
Supply a datestamp to select the first revision that matches the date.
507
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
508
Matches the first entry after a given date (either at midnight or
509
at a specified time).
511
One way to display all the changes since yesterday would be:
512
bzr log -r date:yesterday..-1
515
date:yesterday -> select the first revision since yesterday
516
date:2006-08-14,17:10:14 -> select the first revision after
517
August 14th, 2006 at 5:10pm.
401
520
_date_re = re.compile(
402
521
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
407
526
def _match_on(self, branch, revs):
409
Spec for date revisions:
527
"""Spec for date revisions:
411
529
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
412
530
matches the first entry after a given date (either at midnight or
413
531
at a specified time).
415
So the proper way of saying 'give me all entries for today' is:
416
-r date:yesterday..date:today
533
# XXX: This doesn't actually work
534
# So the proper way of saying 'give me all entries for today' is:
535
# -r date:yesterday..date:today
418
536
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
419
537
if self.spec.lower() == 'yesterday':
420
538
dt = today - datetime.timedelta(days=1)
469
587
class RevisionSpec_ancestor(RevisionSpec):
588
"""Selects a common ancestor with a second branch."""
590
help_txt = """Selects a common ancestor with a second branch.
592
Supply the path to a branch to select the common ancestor.
594
The common ancestor is the last revision that existed in both
595
branches. Usually this is the branch point, but it could also be
596
a revision that was merged.
598
This is frequently used with 'diff' to return all of the changes
599
that your branch introduces, while excluding the changes that you
600
have not merged from the remote branch.
603
ancestor:/path/to/branch
604
$ bzr diff -r ancestor:../../mainline/branch
470
606
prefix = 'ancestor:'
472
608
def _match_on(self, branch, revs):
495
631
class RevisionSpec_branch(RevisionSpec):
496
"""A branch: revision specifier.
498
This takes the path to a branch and returns its tip revision id.
632
"""Selects the last revision of a specified branch."""
634
help_txt = """Selects the last revision of a specified branch.
636
Supply the path to a branch to select its last revision.
639
branch:/path/to/branch
500
641
prefix = 'branch:'