~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
    revision,
25
25
    symbol_versioning,
26
26
    trace,
 
27
    tsort,
27
28
    )
28
29
 
29
30
 
31
32
 
32
33
 
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."""
 
36
 
 
37
    help_txt = """The results of applying a revision specification to a branch.
35
38
 
36
39
    An instance has two useful attributes: revno, and rev_id.
37
40
 
98
101
 
99
102
 
100
103
class RevisionSpec(object):
101
 
    """A parsed revision specification.
 
104
    """A parsed revision specification."""
 
105
 
 
106
    help_txt = """A parsed revision specification.
102
107
 
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
152
157
        else:
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)
160
167
 
199
206
        if branch:
200
207
            revs = branch.revision_history()
201
208
        else:
 
209
            # this should never trigger.
 
210
            # TODO: make it a deprecated code path. RBC 20060928
202
211
            revs = None
203
212
        return self._match_on_and_check(branch, revs)
204
213
 
235
244
# private API
236
245
 
237
246
class RevisionSpec_revno(RevisionSpec):
 
247
    """Selects a revision using a number."""
 
248
 
 
249
    help_txt = """Selects a revision using a number.
 
250
 
 
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.
 
256
    examples:
 
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
 
262
                                   remote branch.
 
263
      -1000000                  -> Most likely the first revision, unless
 
264
                                   your history is very long.
 
265
    """
238
266
    prefix = 'revno:'
239
267
 
240
268
    def _match_on(self, branch, revs):
255
283
        else:
256
284
            try:
257
285
                revno = int(revno_spec)
258
 
            except ValueError, e:
259
 
                raise errors.InvalidRevisionSpec(self.user_spec,
260
 
                                                 branch, e)
 
286
                dotted = False
 
287
            except ValueError:
 
288
                # dotted decimal. This arguably should not be here
 
289
                # but the from_string method is a little primitive 
 
290
                # right now - RBC 20060928
 
291
                try:
 
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)
 
295
 
 
296
                dotted = True
261
297
 
262
298
        if branch_spec:
 
299
            # the user has override the branch to look in.
 
300
            # we need to refresh the revision_history map and
 
301
            # the branch object.
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()
268
307
 
269
 
        if revno < 0:
270
 
            if (-revno) >= len(revs):
271
 
                revno = 1
 
308
        if dotted:
 
309
            branch.lock_read()
 
310
            try:
 
311
                last_rev = branch.last_revision()
 
312
                merge_sorted_revisions = tsort.merge_sort(
 
313
                    branch.repository.get_revision_graph(last_rev),
 
314
                    last_rev,
 
315
                    generate_revno=True)
 
316
                def match(item):
 
317
                    return item[3] == match_revno
 
318
                revisions = filter(match, merge_sorted_revisions)
 
319
            finally:
 
320
                branch.unlock()
 
321
            if len(revisions) != 1:
 
322
                return RevisionInfo(branch, None, None)
272
323
            else:
273
 
                revno = len(revs) + revno + 1
274
 
        try:
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])
 
327
        else:
 
328
            if revno < 0:
 
329
                if (-revno) >= len(revs):
 
330
                    revno = 1
 
331
                else:
 
332
                    revno = len(revs) + revno + 1
 
333
            try:
 
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)
279
338
        
280
339
    def needs_branch(self):
293
352
 
294
353
 
295
354
class RevisionSpec_revid(RevisionSpec):
 
355
    """Selects a revision using the revision id."""
 
356
 
 
357
    help_txt = """Selects a revision using the revision id.
 
358
 
 
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.
 
362
    examples:
 
363
      revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
 
364
    """    
296
365
    prefix = 'revid:'
297
366
 
298
367
    def _match_on(self, branch, revs):
306
375
 
307
376
 
308
377
class RevisionSpec_last(RevisionSpec):
 
378
    """Selects the nth revision from the end."""
 
379
 
 
380
    help_txt = """Selects the nth revision from the end.
 
381
 
 
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.
 
384
    examples:
 
385
      last:1        -> return the last revision
 
386
      last:3        -> return the revision 2 before the end.
 
387
    """    
309
388
 
310
389
    prefix = 'last:'
311
390
 
334
413
 
335
414
 
336
415
class RevisionSpec_before(RevisionSpec):
 
416
    """Selects the parent of the revision specified."""
 
417
 
 
418
    help_txt = """Selects the parent of the revision specified.
 
419
 
 
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.
 
424
 
 
425
    examples:
 
426
      before:1913    -> Return the parent of revno 1913 (revno 1912)
 
427
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
 
428
                                            aaaa@bbbb-1234567890
 
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)
 
432
    """
337
433
 
338
434
    prefix = 'before:'
339
435
    
367
463
 
368
464
 
369
465
class RevisionSpec_tag(RevisionSpec):
 
466
    """To be implemented."""
 
467
 
 
468
    help_txt = """To be implemented."""
 
469
 
370
470
    prefix = 'tag:'
371
471
 
372
472
    def _match_on(self, branch, revs):
397
497
 
398
498
 
399
499
class RevisionSpec_date(RevisionSpec):
 
500
    """Selects a revision on the basis of a datestamp."""
 
501
 
 
502
    help_txt = """Selects a revision on the basis of a datestamp.
 
503
 
 
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).
 
508
 
 
509
    One way to display all the changes since yesterday would be:
 
510
        bzr log -r date:yesterday..-1
 
511
 
 
512
    examples:
 
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.
 
516
    """    
400
517
    prefix = 'date:'
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))?'
405
522
        )
406
523
 
407
524
    def _match_on(self, branch, revs):
408
 
        """
409
 
        Spec for date revisions:
 
525
        """Spec for date revisions:
410
526
          date:value
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).
414
 
 
415
 
          So the proper way of saying 'give me all entries for today' is:
416
 
              -r date:yesterday..date:today
417
530
        """
 
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)
467
583
 
468
584
 
469
585
class RevisionSpec_ancestor(RevisionSpec):
 
586
    """Selects a common ancestor with a second branch."""
 
587
 
 
588
    help_txt = """Selects a common ancestor with a second branch.
 
589
 
 
590
    Supply the path to a branch to select the common ancestor.
 
591
 
 
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.
 
595
 
 
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.
 
599
 
 
600
    examples:
 
601
      ancestor:/path/to/branch
 
602
      $ bzr diff -r ancestor:../../mainline/branch
 
603
    """
470
604
    prefix = 'ancestor:'
471
605
 
472
606
    def _match_on(self, branch, revs):
493
627
 
494
628
 
495
629
class RevisionSpec_branch(RevisionSpec):
496
 
    """A branch: revision specifier.
497
 
 
498
 
    This takes the path to a branch and returns its tip revision id.
 
630
    """Selects the last revision of a specified branch."""
 
631
 
 
632
    help_txt = """Selects the last revision of a specified branch.
 
633
 
 
634
    Supply the path to a branch to select its last revision.
 
635
 
 
636
    examples:
 
637
      branch:/path/to/branch
499
638
    """
500
639
    prefix = 'branch:'
501
640