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 (-revno) >= len(revs):
332
revno = len(revs) + revno + 1
334
revision_id = branch.get_rev_id(revno, revs)
335
except errors.NoSuchRevision:
336
raise errors.InvalidRevisionSpec(self.user_spec, branch)
278
337
return RevisionInfo(branch, revno, revision_id)
280
339
def needs_branch(self):
295
354
class RevisionSpec_revid(RevisionSpec):
355
"""Selects a revision using the revision id."""
357
help_txt = """Selects a revision using the revision id.
359
Supply a specific revision id, that can be used to specify any
360
revision id in the ancestry of the branch.
361
Including merges, and pending merges.
363
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
296
365
prefix = 'revid:'
298
367
def _match_on(self, branch, revs):
308
377
class RevisionSpec_last(RevisionSpec):
378
"""Selects the nth revision from the end."""
380
help_txt = """Selects the nth revision from the end.
382
Supply a positive number to get the nth revision from the end.
383
This is the same as supplying negative numbers to the 'revno:' spec.
385
last:1 -> return the last revision
386
last:3 -> return the revision 2 before the end.
336
415
class RevisionSpec_before(RevisionSpec):
416
"""Selects the parent of the revision specified."""
418
help_txt = """Selects the parent of the revision specified.
420
Supply any revision spec to return the parent of that revision.
421
It is an error to request the parent of the null revision (before:0).
422
This is mostly useful when inspecting revisions that are not in the
423
revision history of a branch.
426
before:1913 -> Return the parent of revno 1913 (revno 1912)
427
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
429
bzr diff -r before:revid:aaaa..revid:aaaa
430
-> Find the changes between revision 'aaaa' and its parent.
431
(what changes did 'aaaa' introduce)
338
434
prefix = 'before:'
399
499
class RevisionSpec_date(RevisionSpec):
500
"""Selects a revision on the basis of a datestamp."""
502
help_txt = """Selects a revision on the basis of a datestamp.
504
Supply a datestamp to select the first revision that matches the date.
505
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
506
Matches the first entry after a given date (either at midnight or
507
at a specified time).
509
One way to display all the changes since yesterday would be:
510
bzr log -r date:yesterday..-1
513
date:yesterday -> select the first revision since yesterday
514
date:2006-08-14,17:10:14 -> select the first revision after
515
August 14th, 2006 at 5:10pm.
401
518
_date_re = re.compile(
402
519
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
407
524
def _match_on(self, branch, revs):
409
Spec for date revisions:
525
"""Spec for date revisions:
411
527
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
412
528
matches the first entry after a given date (either at midnight or
413
529
at a specified time).
415
So the proper way of saying 'give me all entries for today' is:
416
-r date:yesterday..date:today
531
# XXX: This doesn't actually work
532
# So the proper way of saying 'give me all entries for today' is:
533
# -r date:yesterday..date:today
418
534
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
419
535
if self.spec.lower() == 'yesterday':
420
536
dt = today - datetime.timedelta(days=1)
469
585
class RevisionSpec_ancestor(RevisionSpec):
586
"""Selects a common ancestor with a second branch."""
588
help_txt = """Selects a common ancestor with a second branch.
590
Supply the path to a branch to select the common ancestor.
592
The common ancestor is the last revision that existed in both
593
branches. Usually this is the branch point, but it could also be
594
a revision that was merged.
596
This is frequently used with 'diff' to return all of the changes
597
that your branch introduces, while excluding the changes that you
598
have not merged from the remote branch.
601
ancestor:/path/to/branch
602
$ bzr diff -r ancestor:../../mainline/branch
470
604
prefix = 'ancestor:'
472
606
def _match_on(self, branch, revs):
495
629
class RevisionSpec_branch(RevisionSpec):
496
"""A branch: revision specifier.
498
This takes the path to a branch and returns its tip revision id.
630
"""Selects the last revision of a specified branch."""
632
help_txt = """Selects the last revision of a specified branch.
634
Supply the path to a branch to select its last revision.
637
branch:/path/to/branch
500
639
prefix = 'branch:'