~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/revisionspec.py

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge 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 get_rev_id supported negative revnos, there would not be a
 
330
                # need for this special case.
 
331
                if (-revno) >= len(revs):
 
332
                    revno = 1
 
333
                else:
 
334
                    revno = len(revs) + revno + 1
 
335
            try:
 
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)
279
340
        
280
341
    def needs_branch(self):
293
354
 
294
355
 
295
356
class RevisionSpec_revid(RevisionSpec):
 
357
    """Selects a revision using the revision id."""
 
358
 
 
359
    help_txt = """Selects a revision using the revision id.
 
360
 
 
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.
 
364
    examples:
 
365
      revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
 
366
    """    
296
367
    prefix = 'revid:'
297
368
 
298
369
    def _match_on(self, branch, revs):
306
377
 
307
378
 
308
379
class RevisionSpec_last(RevisionSpec):
 
380
    """Selects the nth revision from the end."""
 
381
 
 
382
    help_txt = """Selects the nth revision from the end.
 
383
 
 
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.
 
386
    examples:
 
387
      last:1        -> return the last revision
 
388
      last:3        -> return the revision 2 before the end.
 
389
    """    
309
390
 
310
391
    prefix = 'last:'
311
392
 
334
415
 
335
416
 
336
417
class RevisionSpec_before(RevisionSpec):
 
418
    """Selects the parent of the revision specified."""
 
419
 
 
420
    help_txt = """Selects the parent of the revision specified.
 
421
 
 
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.
 
426
 
 
427
    examples:
 
428
      before:1913    -> Return the parent of revno 1913 (revno 1912)
 
429
      before:revid:aaaa@bbbb-1234567890  -> return the parent of revision
 
430
                                            aaaa@bbbb-1234567890
 
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)
 
434
    """
337
435
 
338
436
    prefix = 'before:'
339
437
    
367
465
 
368
466
 
369
467
class RevisionSpec_tag(RevisionSpec):
 
468
    """To be implemented."""
 
469
 
 
470
    help_txt = """To be implemented."""
 
471
 
370
472
    prefix = 'tag:'
371
473
 
372
474
    def _match_on(self, branch, revs):
397
499
 
398
500
 
399
501
class RevisionSpec_date(RevisionSpec):
 
502
    """Selects a revision on the basis of a datestamp."""
 
503
 
 
504
    help_txt = """Selects a revision on the basis of a datestamp.
 
505
 
 
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).
 
510
 
 
511
    One way to display all the changes since yesterday would be:
 
512
        bzr log -r date:yesterday..-1
 
513
 
 
514
    examples:
 
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.
 
518
    """    
400
519
    prefix = 'date:'
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))?'
405
524
        )
406
525
 
407
526
    def _match_on(self, branch, revs):
408
 
        """
409
 
        Spec for date revisions:
 
527
        """Spec for date revisions:
410
528
          date:value
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).
414
 
 
415
 
          So the proper way of saying 'give me all entries for today' is:
416
 
              -r date:yesterday..date:today
417
532
        """
 
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)
467
585
 
468
586
 
469
587
class RevisionSpec_ancestor(RevisionSpec):
 
588
    """Selects a common ancestor with a second branch."""
 
589
 
 
590
    help_txt = """Selects a common ancestor with a second branch.
 
591
 
 
592
    Supply the path to a branch to select the common ancestor.
 
593
 
 
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.
 
597
 
 
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.
 
601
 
 
602
    examples:
 
603
      ancestor:/path/to/branch
 
604
      $ bzr diff -r ancestor:../../mainline/branch
 
605
    """
470
606
    prefix = 'ancestor:'
471
607
 
472
608
    def _match_on(self, branch, revs):
493
629
 
494
630
 
495
631
class RevisionSpec_branch(RevisionSpec):
496
 
    """A branch: revision specifier.
497
 
 
498
 
    This takes the path to a branch and returns its tip revision id.
 
632
    """Selects the last revision of a specified branch."""
 
633
 
 
634
    help_txt = """Selects the last revision of a specified branch.
 
635
 
 
636
    Supply the path to a branch to select its last revision.
 
637
 
 
638
    examples:
 
639
      branch:/path/to/branch
499
640
    """
500
641
    prefix = 'branch:'
501
642