~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-06-22 09:35:24 UTC
  • Revision ID: mbp@sourcefrog.net-20050622093524-b15e2d374c2ae6ea
- move standard plugins from contrib/plugins to just ./plugins

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import sys, os
20
20
 
21
21
import bzrlib
22
 
from bzrlib.trace import mutter, note, log_error, warning
 
22
from bzrlib.trace import mutter, note, log_error
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
 
from bzrlib.branch import find_branch
25
 
from bzrlib import BZRDIR
 
24
from bzrlib.osutils import quotefn
 
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
 
26
     format_date
26
27
 
27
28
 
28
29
plugin_cmds = {}
51
52
    assert cmd.startswith("cmd_")
52
53
    return cmd[4:].replace('_','-')
53
54
 
54
 
 
55
55
def _parse_revision_str(revstr):
56
 
    """This handles a revision string -> revno.
57
 
 
58
 
    This always returns a list.  The list will have one element for 
59
 
 
60
 
    It supports integers directly, but everything else it
61
 
    defers for passing to Branch.get_revision_info()
62
 
 
63
 
    >>> _parse_revision_str('234')
64
 
    [234]
65
 
    >>> _parse_revision_str('234..567')
66
 
    [234, 567]
67
 
    >>> _parse_revision_str('..')
68
 
    [None, None]
69
 
    >>> _parse_revision_str('..234')
70
 
    [None, 234]
71
 
    >>> _parse_revision_str('234..')
72
 
    [234, None]
73
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
74
 
    [234, 456, 789]
75
 
    >>> _parse_revision_str('234....789') # Error?
76
 
    [234, None, 789]
77
 
    >>> _parse_revision_str('revid:test@other.com-234234')
78
 
    ['revid:test@other.com-234234']
79
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
80
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
81
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
82
 
    ['revid:test@other.com-234234', 23]
83
 
    >>> _parse_revision_str('date:2005-04-12')
84
 
    ['date:2005-04-12']
85
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
86
 
    ['date:2005-04-12 12:24:33']
87
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
88
 
    ['date:2005-04-12T12:24:33']
89
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
90
 
    ['date:2005-04-12,12:24:33']
91
 
    >>> _parse_revision_str('-5..23')
92
 
    [-5, 23]
93
 
    >>> _parse_revision_str('-5')
94
 
    [-5]
95
 
    >>> _parse_revision_str('123a')
96
 
    ['123a']
97
 
    >>> _parse_revision_str('abc')
98
 
    ['abc']
 
56
    """This handles a revision string -> revno. 
 
57
 
 
58
    There are several possibilities:
 
59
 
 
60
        '234'       -> 234
 
61
        '234:345'   -> [234, 345]
 
62
        ':234'      -> [None, 234]
 
63
        '234:'      -> [234, None]
 
64
 
 
65
    In the future we will also support:
 
66
        'uuid:blah-blah-blah'   -> ?
 
67
        'hash:blahblahblah'     -> ?
 
68
        potentially:
 
69
        'tag:mytag'             -> ?
99
70
    """
100
 
    import re
101
 
    old_format_re = re.compile('\d*:\d*')
102
 
    m = old_format_re.match(revstr)
103
 
    if m:
104
 
        warning('Colon separator for revision numbers is deprecated.'
105
 
                ' Use .. instead')
106
 
        revs = []
107
 
        for rev in revstr.split(':'):
108
 
            if rev:
109
 
                revs.append(int(rev))
110
 
            else:
111
 
                revs.append(None)
112
 
        return revs
113
 
    revs = []
114
 
    for x in revstr.split('..'):
115
 
        if not x:
116
 
            revs.append(None)
117
 
        else:
118
 
            try:
119
 
                revs.append(int(x))
120
 
            except ValueError:
121
 
                revs.append(x)
 
71
    if revstr.find(':') != -1:
 
72
        revs = revstr.split(':')
 
73
        if len(revs) > 2:
 
74
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
 
75
 
 
76
        if not revs[0]:
 
77
            revs[0] = None
 
78
        else:
 
79
            revs[0] = int(revs[0])
 
80
 
 
81
        if not revs[1]:
 
82
            revs[1] = None
 
83
        else:
 
84
            revs[1] = int(revs[1])
 
85
    else:
 
86
        revs = int(revstr)
122
87
    return revs
123
88
 
124
89
 
332
297
    directory is shown.  Otherwise, only the status of the specified
333
298
    files or directories is reported.  If a directory is given, status
334
299
    is reported for everything inside that directory.
335
 
 
336
 
    If a revision is specified, the changes since that revision are shown.
337
300
    """
338
301
    takes_args = ['file*']
339
 
    takes_options = ['all', 'show-ids', 'revision']
 
302
    takes_options = ['all', 'show-ids']
340
303
    aliases = ['st', 'stat']
341
304
    
342
305
    def run(self, all=False, show_ids=False, file_list=None):
343
306
        if file_list:
344
 
            b = find_branch(file_list[0])
 
307
            b = Branch(file_list[0])
345
308
            file_list = [b.relpath(x) for x in file_list]
346
309
            # special case: only one path was given and it's the root
347
310
            # of the branch
348
311
            if file_list == ['']:
349
312
                file_list = None
350
313
        else:
351
 
            b = find_branch('.')
352
 
            
353
 
        from bzrlib.status import show_status
354
 
        show_status(b, show_unchanged=all, show_ids=show_ids,
355
 
                    specific_files=file_list)
 
314
            b = Branch('.')
 
315
        import status
 
316
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
 
317
                           specific_files=file_list)
356
318
 
357
319
 
358
320
class cmd_cat_revision(Command):
362
324
    takes_args = ['revision_id']
363
325
    
364
326
    def run(self, revision_id):
365
 
        from bzrlib.xml import pack_xml
366
 
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
 
327
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
367
328
 
368
329
 
369
330
class cmd_revno(Command):
371
332
 
372
333
    This is equal to the number of revisions on this branch."""
373
334
    def run(self):
374
 
        print find_branch('.').revno()
375
 
 
376
 
class cmd_revision_info(Command):
377
 
    """Show revision number and revision id for a given revision identifier.
378
 
    """
379
 
    hidden = True
380
 
    takes_args = ['revision_info*']
381
 
    takes_options = ['revision']
382
 
    def run(self, revision=None, revision_info_list=None):
383
 
        from bzrlib.branch import find_branch
384
 
 
385
 
        revs = []
386
 
        if revision is not None:
387
 
            revs.extend(revision)
388
 
        if revision_info_list is not None:
389
 
            revs.extend(revision_info_list)
390
 
        if len(revs) == 0:
391
 
            raise BzrCommandError('You must supply a revision identifier')
392
 
 
393
 
        b = find_branch('.')
394
 
 
395
 
        for rev in revs:
396
 
            print '%4d %s' % b.get_revision_info(rev)
 
335
        print Branch('.').revno()
397
336
 
398
337
    
399
338
class cmd_add(Command):
421
360
    takes_options = ['verbose', 'no-recurse']
422
361
    
423
362
    def run(self, file_list, verbose=False, no_recurse=False):
424
 
        from bzrlib.add import smart_add
425
 
        smart_add(file_list, verbose, not no_recurse)
 
363
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
426
364
 
427
365
 
428
366
 
434
372
    takes_args = ['dir+']
435
373
 
436
374
    def run(self, dir_list):
 
375
        import os
 
376
        import bzrlib.branch
 
377
        
437
378
        b = None
438
379
        
439
380
        for d in dir_list:
440
381
            os.mkdir(d)
441
382
            if not b:
442
 
                b = find_branch(d)
 
383
                b = bzrlib.branch.Branch(d)
443
384
            b.add([d], verbose=True)
444
385
 
445
386
 
449
390
    hidden = True
450
391
    
451
392
    def run(self, filename):
452
 
        print find_branch(filename).relpath(filename)
 
393
        print Branch(filename).relpath(filename)
453
394
 
454
395
 
455
396
 
458
399
    takes_options = ['revision', 'show-ids']
459
400
    
460
401
    def run(self, revision=None, show_ids=False):
461
 
        b = find_branch('.')
 
402
        b = Branch('.')
462
403
        if revision == None:
463
404
            inv = b.read_working_inventory()
464
405
        else:
465
 
            if len(revision) > 1:
466
 
                raise BzrCommandError('bzr inventory --revision takes'
467
 
                    ' exactly one revision identifier')
468
 
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
 
406
            inv = b.get_revision_inventory(b.lookup_revision(revision))
469
407
 
470
408
        for path, entry in inv.entries():
471
409
            if show_ids:
484
422
    """
485
423
    takes_args = ['source$', 'dest']
486
424
    def run(self, source_list, dest):
487
 
        b = find_branch('.')
 
425
        b = Branch('.')
488
426
 
489
427
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
490
428
 
506
444
    takes_args = ['from_name', 'to_name']
507
445
    
508
446
    def run(self, from_name, to_name):
509
 
        b = find_branch('.')
 
447
        b = Branch('.')
510
448
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
511
449
 
512
450
 
531
469
 
532
470
    def run(self, location=None):
533
471
        from bzrlib.merge import merge
534
 
        import tempfile
535
 
        from shutil import rmtree
536
472
        import errno
537
473
        
538
 
        br_to = find_branch('.')
 
474
        br_to = Branch('.')
539
475
        stored_loc = None
540
476
        try:
541
477
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
542
478
        except IOError, e:
543
 
            if e.errno != errno.ENOENT:
 
479
            if errno == errno.ENOENT:
544
480
                raise
545
481
        if location is None:
546
482
            if stored_loc is None:
548
484
            else:
549
485
                print "Using last location: %s" % stored_loc
550
486
                location = stored_loc
551
 
        cache_root = tempfile.mkdtemp()
552
 
        from bzrlib.branch import DivergedBranches
 
487
        from branch import find_branch, DivergedBranches
553
488
        br_from = find_branch(location)
554
489
        location = pull_loc(br_from)
555
490
        old_revno = br_to.revno()
556
491
        try:
557
 
            from branch import find_cached_branch, DivergedBranches
558
 
            br_from = find_cached_branch(location, cache_root)
559
 
            location = pull_loc(br_from)
560
 
            old_revno = br_to.revno()
561
 
            try:
562
 
                br_to.update_revisions(br_from)
563
 
            except DivergedBranches:
564
 
                raise BzrCommandError("These branches have diverged."
565
 
                    "  Try merge.")
566
 
                
567
 
            merge(('.', -1), ('.', old_revno), check_clean=False)
568
 
            if location != stored_loc:
569
 
                br_to.controlfile("x-pull", "wb").write(location + "\n")
570
 
        finally:
571
 
            rmtree(cache_root)
 
492
            br_to.update_revisions(br_from)
 
493
        except DivergedBranches:
 
494
            raise BzrCommandError("These branches have diverged.  Try merge.")
 
495
            
 
496
        merge(('.', -1), ('.', old_revno), check_clean=False)
 
497
        if location != stored_loc:
 
498
            br_to.controlfile("x-pull", "wb").write(location + "\n")
572
499
 
573
500
 
574
501
 
587
514
    def run(self, from_location, to_location=None, revision=None):
588
515
        import errno
589
516
        from bzrlib.merge import merge
590
 
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
591
 
             find_cached_branch, Branch
 
517
        from branch import find_branch, DivergedBranches, NoSuchRevision
592
518
        from shutil import rmtree
593
 
        from meta_store import CachedStore
594
 
        import tempfile
595
 
        cache_root = tempfile.mkdtemp()
596
 
 
597
 
        if revision is None:
598
 
            revision = [None]
599
 
        elif len(revision) > 1:
600
 
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
601
 
 
602
 
        try:
603
 
            try:
604
 
                br_from = find_cached_branch(from_location, cache_root)
605
 
            except OSError, e:
606
 
                if e.errno == errno.ENOENT:
607
 
                    raise BzrCommandError('Source location "%s" does not'
608
 
                                          ' exist.' % to_location)
609
 
                else:
610
 
                    raise
611
 
 
612
 
            if to_location is None:
613
 
                to_location = os.path.basename(from_location.rstrip("/\\"))
614
 
 
615
 
            try:
616
 
                os.mkdir(to_location)
617
 
            except OSError, e:
618
 
                if e.errno == errno.EEXIST:
619
 
                    raise BzrCommandError('Target directory "%s" already'
620
 
                                          ' exists.' % to_location)
621
 
                if e.errno == errno.ENOENT:
622
 
                    raise BzrCommandError('Parent of "%s" does not exist.' %
623
 
                                          to_location)
624
 
                else:
625
 
                    raise
626
 
            br_to = Branch(to_location, init=True)
627
 
 
628
 
            br_to.set_root_id(br_from.get_root_id())
629
 
 
630
 
            if revision:
631
 
                if revision[0] is None:
632
 
                    revno = br_from.revno()
633
 
                else:
634
 
                    revno, rev_id = br_from.get_revision_info(revision[0])
635
 
                try:
636
 
                    br_to.update_revisions(br_from, stop_revision=revno)
637
 
                except NoSuchRevision:
638
 
                    rmtree(to_location)
639
 
                    msg = "The branch %s has no revision %d." % (from_location,
640
 
                                                                 revno)
641
 
                    raise BzrCommandError(msg)
642
 
            
643
 
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
644
 
                  check_clean=False, ignore_zero=True)
645
 
            from_location = pull_loc(br_from)
646
 
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
647
 
        finally:
648
 
            rmtree(cache_root)
 
519
        try:
 
520
            br_from = find_branch(from_location)
 
521
        except OSError, e:
 
522
            if e.errno == errno.ENOENT:
 
523
                raise BzrCommandError('Source location "%s" does not exist.' %
 
524
                                      to_location)
 
525
            else:
 
526
                raise
 
527
 
 
528
        if to_location is None:
 
529
            to_location = os.path.basename(from_location.rstrip("/\\"))
 
530
 
 
531
        try:
 
532
            os.mkdir(to_location)
 
533
        except OSError, e:
 
534
            if e.errno == errno.EEXIST:
 
535
                raise BzrCommandError('Target directory "%s" already exists.' %
 
536
                                      to_location)
 
537
            if e.errno == errno.ENOENT:
 
538
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
539
                                      to_location)
 
540
            else:
 
541
                raise
 
542
        br_to = Branch(to_location, init=True)
 
543
 
 
544
        try:
 
545
            br_to.update_revisions(br_from, stop_revision=revision)
 
546
        except NoSuchRevision:
 
547
            rmtree(to_location)
 
548
            msg = "The branch %s has no revision %d." % (from_location,
 
549
                                                         revision)
 
550
            raise BzrCommandError(msg)
 
551
        merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
552
              check_clean=False, ignore_zero=True)
 
553
        from_location = pull_loc(br_from)
 
554
        br_to.controlfile("x-pull", "wb").write(from_location + "\n")
649
555
 
650
556
 
651
557
def pull_loc(branch):
668
574
    takes_args = ['dir?']
669
575
 
670
576
    def run(self, dir='.'):
671
 
        b = find_branch(dir)
 
577
        b = Branch(dir)
672
578
        old_inv = b.basis_tree().inventory
673
579
        new_inv = b.read_working_inventory()
674
580
 
685
591
    def run(self, branch=None):
686
592
        import info
687
593
 
 
594
        from branch import find_branch
688
595
        b = find_branch(branch)
689
596
        info.show_info(b)
690
597
 
699
606
    takes_options = ['verbose']
700
607
    
701
608
    def run(self, file_list, verbose=False):
702
 
        b = find_branch(file_list[0])
 
609
        b = Branch(file_list[0])
703
610
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
704
611
 
705
612
 
713
620
    hidden = True
714
621
    takes_args = ['filename']
715
622
    def run(self, filename):
716
 
        b = find_branch(filename)
 
623
        b = Branch(filename)
717
624
        i = b.inventory.path2id(b.relpath(filename))
718
625
        if i == None:
719
626
            raise BzrError("%r is not a versioned file" % filename)
729
636
    hidden = True
730
637
    takes_args = ['filename']
731
638
    def run(self, filename):
732
 
        b = find_branch(filename)
 
639
        b = Branch(filename)
733
640
        inv = b.inventory
734
641
        fid = inv.path2id(b.relpath(filename))
735
642
        if fid == None:
742
649
    """Display list of revision ids on this branch."""
743
650
    hidden = True
744
651
    def run(self):
745
 
        for patchid in find_branch('.').revision_history():
 
652
        for patchid in Branch('.').revision_history():
746
653
            print patchid
747
654
 
748
655
 
749
656
class cmd_directories(Command):
750
657
    """Display list of versioned directories in this branch."""
751
658
    def run(self):
752
 
        for name, ie in find_branch('.').read_working_inventory().directories():
 
659
        for name, ie in Branch('.').read_working_inventory().directories():
753
660
            if name == '':
754
661
                print '.'
755
662
            else:
770
677
        bzr commit -m 'imported project'
771
678
    """
772
679
    def run(self):
773
 
        from bzrlib.branch import Branch
774
680
        Branch('.', init=True)
775
681
 
776
682
 
804
710
 
805
711
    def run(self, revision=None, file_list=None, diff_options=None):
806
712
        from bzrlib.diff import show_diff
 
713
        from bzrlib import find_branch
807
714
 
808
715
        if file_list:
809
716
            b = find_branch(file_list[0])
812
719
                # just pointing to top-of-tree
813
720
                file_list = None
814
721
        else:
815
 
            b = find_branch('.')
816
 
 
817
 
        # TODO: Make show_diff support taking 2 arguments
818
 
        base_rev = None
819
 
        if revision is not None:
820
 
            if len(revision) != 1:
821
 
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
822
 
            base_rev = revision[0]
 
722
            b = Branch('.')
823
723
    
824
 
        show_diff(b, base_rev, specific_files=file_list,
 
724
        show_diff(b, revision, specific_files=file_list,
825
725
                  external_diff_options=diff_options)
826
726
 
827
727
 
834
734
    TODO: Show files deleted since a previous revision, or between two revisions.
835
735
    """
836
736
    def run(self, show_ids=False):
837
 
        b = find_branch('.')
 
737
        b = Branch('.')
838
738
        old = b.basis_tree()
839
739
        new = b.working_tree()
840
740
 
855
755
    """List files modified in working tree."""
856
756
    hidden = True
857
757
    def run(self):
858
 
        from bzrlib.diff import compare_trees
859
 
 
860
 
        b = find_branch('.')
861
 
        td = compare_trees(b.basis_tree(), b.working_tree())
862
 
 
863
 
        for path, id, kind in td.modified:
864
 
            print path
 
758
        import statcache
 
759
        b = Branch('.')
 
760
        inv = b.read_working_inventory()
 
761
        sc = statcache.update_cache(b, inv)
 
762
        basis = b.basis_tree()
 
763
        basis_inv = basis.inventory
 
764
        
 
765
        # We used to do this through iter_entries(), but that's slow
 
766
        # when most of the files are unmodified, as is usually the
 
767
        # case.  So instead we iterate by inventory entry, and only
 
768
        # calculate paths as necessary.
 
769
 
 
770
        for file_id in basis_inv:
 
771
            cacheentry = sc.get(file_id)
 
772
            if not cacheentry:                 # deleted
 
773
                continue
 
774
            ie = basis_inv[file_id]
 
775
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
 
776
                path = inv.id2path(file_id)
 
777
                print path
865
778
 
866
779
 
867
780
 
869
782
    """List files added in working tree."""
870
783
    hidden = True
871
784
    def run(self):
872
 
        b = find_branch('.')
 
785
        b = Branch('.')
873
786
        wt = b.working_tree()
874
787
        basis_inv = b.basis_tree().inventory
875
788
        inv = wt.inventory
891
804
    takes_args = ['filename?']
892
805
    def run(self, filename=None):
893
806
        """Print the branch root."""
 
807
        from branch import find_branch
894
808
        b = find_branch(filename)
895
809
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
896
810
 
902
816
    -r revision requests a specific revision, -r :end or -r begin: are
903
817
    also valid.
904
818
 
905
 
    --message allows you to give a regular expression, which will be evaluated
906
 
    so that only matching entries will be displayed.
907
 
 
908
819
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
909
820
  
910
821
    """
911
822
 
912
823
    takes_args = ['filename?']
913
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
 
824
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
914
825
    
915
826
    def run(self, filename=None, timezone='original',
916
827
            verbose=False,
917
828
            show_ids=False,
918
829
            forward=False,
919
 
            revision=None,
920
 
            message=None,
921
 
            long=False):
922
 
        from bzrlib.branch import find_branch
923
 
        from bzrlib.log import log_formatter, show_log
 
830
            revision=None):
 
831
        from bzrlib import show_log, find_branch
924
832
        import codecs
925
833
 
926
834
        direction = (forward and 'forward') or 'reverse'
936
844
            b = find_branch('.')
937
845
            file_id = None
938
846
 
939
 
        if revision is None:
940
 
            rev1 = None
941
 
            rev2 = None
942
 
        elif len(revision) == 1:
943
 
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
944
 
        elif len(revision) == 2:
945
 
            rev1 = b.get_revision_info(revision[0])[0]
946
 
            rev2 = b.get_revision_info(revision[1])[0]
 
847
        if revision == None:
 
848
            revision = [None, None]
 
849
        elif isinstance(revision, int):
 
850
            revision = [revision, revision]
947
851
        else:
948
 
            raise BzrCommandError('bzr log --revision takes one or two values.')
949
 
 
950
 
        if rev1 == 0:
951
 
            rev1 = None
952
 
        if rev2 == 0:
953
 
            rev2 = None
 
852
            # pair of revisions?
 
853
            pass
 
854
            
 
855
        assert len(revision) == 2
954
856
 
955
857
        mutter('encoding log as %r' % bzrlib.user_encoding)
956
858
 
958
860
        # in e.g. the default C locale.
959
861
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
960
862
 
961
 
        if long:
962
 
            log_format = 'long'
963
 
        else:
964
 
            log_format = 'short'
965
 
        lf = log_formatter(log_format,
966
 
                           show_ids=show_ids,
967
 
                           to_file=outf,
968
 
                           show_timezone=timezone)
969
 
 
970
 
        show_log(b,
971
 
                 lf,
972
 
                 file_id,
 
863
        show_log(b, file_id,
 
864
                 show_timezone=timezone,
973
865
                 verbose=verbose,
 
866
                 show_ids=show_ids,
 
867
                 to_file=outf,
974
868
                 direction=direction,
975
 
                 start_revision=rev1,
976
 
                 end_revision=rev2,
977
 
                 search=message)
 
869
                 start_revision=revision[0],
 
870
                 end_revision=revision[1])
978
871
 
979
872
 
980
873
 
985
878
    hidden = True
986
879
    takes_args = ["filename"]
987
880
    def run(self, filename):
988
 
        b = find_branch(filename)
 
881
        b = Branch(filename)
989
882
        inv = b.read_working_inventory()
990
883
        file_id = inv.path2id(b.relpath(filename))
991
884
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
999
892
    """
1000
893
    hidden = True
1001
894
    def run(self, revision=None, verbose=False):
1002
 
        b = find_branch('.')
 
895
        b = Branch('.')
1003
896
        if revision == None:
1004
897
            tree = b.working_tree()
1005
898
        else:
1023
916
class cmd_unknowns(Command):
1024
917
    """List unknown files."""
1025
918
    def run(self):
1026
 
        from bzrlib.osutils import quotefn
1027
 
        for f in find_branch('.').unknowns():
 
919
        for f in Branch('.').unknowns():
1028
920
            print quotefn(f)
1029
921
 
1030
922
 
1052
944
        from bzrlib.atomicfile import AtomicFile
1053
945
        import os.path
1054
946
 
1055
 
        b = find_branch('.')
 
947
        b = Branch('.')
1056
948
        ifn = b.abspath('.bzrignore')
1057
949
 
1058
950
        if os.path.exists(ifn):
1092
984
 
1093
985
    See also: bzr ignore"""
1094
986
    def run(self):
1095
 
        tree = find_branch('.').working_tree()
 
987
        tree = Branch('.').working_tree()
1096
988
        for path, file_class, kind, file_id in tree.list_files():
1097
989
            if file_class != 'I':
1098
990
                continue
1116
1008
        except ValueError:
1117
1009
            raise BzrCommandError("not a valid revision-number: %r" % revno)
1118
1010
 
1119
 
        print find_branch('.').lookup_revision(revno)
 
1011
        print Branch('.').lookup_revision(revno)
1120
1012
 
1121
1013
 
1122
1014
class cmd_export(Command):
1125
1017
    If no revision is specified this exports the last committed revision.
1126
1018
 
1127
1019
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1128
 
    given, try to find the format with the extension. If no extension
1129
 
    is found exports to a directory (equivalent to --format=dir).
1130
 
 
1131
 
    Root may be the top directory for tar, tgz and tbz2 formats. If none
1132
 
    is given, the top directory will be the root name of the file."""
 
1020
    given, exports to a directory (equivalent to --format=dir)."""
1133
1021
    # TODO: list known exporters
1134
1022
    takes_args = ['dest']
1135
 
    takes_options = ['revision', 'format', 'root']
1136
 
    def run(self, dest, revision=None, format=None, root=None):
1137
 
        import os.path
1138
 
        b = find_branch('.')
1139
 
        if revision is None:
1140
 
            rev_id = b.last_patch()
 
1023
    takes_options = ['revision', 'format']
 
1024
    def run(self, dest, revision=None, format='dir'):
 
1025
        b = Branch('.')
 
1026
        if revision == None:
 
1027
            rh = b.revision_history()[-1]
1141
1028
        else:
1142
 
            if len(revision) != 1:
1143
 
                raise BzrError('bzr export --revision takes exactly 1 argument')
1144
 
            revno, rev_id = b.get_revision_info(revision[0])
1145
 
        t = b.revision_tree(rev_id)
1146
 
        root, ext = os.path.splitext(dest)
1147
 
        if not format:
1148
 
            if ext in (".tar",):
1149
 
                format = "tar"
1150
 
            elif ext in (".gz", ".tgz"):
1151
 
                format = "tgz"
1152
 
            elif ext in (".bz2", ".tbz2"):
1153
 
                format = "tbz2"
1154
 
            else:
1155
 
                format = "dir"
1156
 
        t.export(dest, format, root)
 
1029
            rh = b.lookup_revision(int(revision))
 
1030
        t = b.revision_tree(rh)
 
1031
        t.export(dest, format)
1157
1032
 
1158
1033
 
1159
1034
class cmd_cat(Command):
1165
1040
    def run(self, filename, revision=None):
1166
1041
        if revision == None:
1167
1042
            raise BzrCommandError("bzr cat requires a revision number")
1168
 
        elif len(revision) != 1:
1169
 
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1170
 
        b = find_branch('.')
1171
 
        b.print_file(b.relpath(filename), revision[0])
 
1043
        b = Branch('.')
 
1044
        b.print_file(b.relpath(filename), int(revision))
1172
1045
 
1173
1046
 
1174
1047
class cmd_local_time_offset(Command):
1195
1068
    TODO: Strict commit that fails if there are unknown or deleted files.
1196
1069
    """
1197
1070
    takes_args = ['selected*']
1198
 
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
1071
    takes_options = ['message', 'file', 'verbose']
1199
1072
    aliases = ['ci', 'checkin']
1200
1073
 
1201
 
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1202
 
            unchanged=False):
1203
 
        from bzrlib.errors import PointlessCommit
1204
 
        from bzrlib.osutils import get_text_message
 
1074
    def run(self, message=None, file=None, verbose=True, selected_list=None):
 
1075
        from bzrlib.commit import commit
1205
1076
 
1206
1077
        ## Warning: shadows builtin file()
1207
1078
        if not message and not file:
1208
 
            import cStringIO
1209
 
            stdout = sys.stdout
1210
 
            catcher = cStringIO.StringIO()
1211
 
            sys.stdout = catcher
1212
 
            cmd_status({"file_list":selected_list}, {})
1213
 
            info = catcher.getvalue()
1214
 
            sys.stdout = stdout
1215
 
            message = get_text_message(info)
1216
 
            
1217
 
            if message is None:
1218
 
                raise BzrCommandError("please specify a commit message",
1219
 
                                      ["use either --message or --file"])
 
1079
            raise BzrCommandError("please specify a commit message",
 
1080
                                  ["use either --message or --file"])
1220
1081
        elif message and file:
1221
1082
            raise BzrCommandError("please specify either --message or --file")
1222
1083
        
1224
1085
            import codecs
1225
1086
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1226
1087
 
1227
 
        b = find_branch('.')
1228
 
 
1229
 
        try:
1230
 
            b.commit(message, verbose=verbose,
1231
 
                     specific_files=selected_list,
1232
 
                     allow_pointless=unchanged)
1233
 
        except PointlessCommit:
1234
 
            # FIXME: This should really happen before the file is read in;
1235
 
            # perhaps prepare the commit; get the message; then actually commit
1236
 
            raise BzrCommandError("no changes to commit",
1237
 
                                  ["use --unchanged to commit anyhow"])
 
1088
        b = Branch('.')
 
1089
        commit(b, message, verbose=verbose, specific_files=selected_list)
1238
1090
 
1239
1091
 
1240
1092
class cmd_check(Command):
1249
1101
    takes_args = ['dir?']
1250
1102
 
1251
1103
    def run(self, dir='.'):
1252
 
        from bzrlib.check import check
1253
 
        check(find_branch(dir))
1254
 
 
1255
 
 
1256
 
 
1257
 
class cmd_scan_cache(Command):
1258
 
    hidden = True
1259
 
    def run(self):
1260
 
        from bzrlib.hashcache import HashCache
1261
 
        import os
1262
 
 
1263
 
        c = HashCache('.')
1264
 
        c.read()
1265
 
        c.scan()
1266
 
            
1267
 
        print '%6d stats' % c.stat_count
1268
 
        print '%6d in hashcache' % len(c._cache)
1269
 
        print '%6d files removed from cache' % c.removed_count
1270
 
        print '%6d hashes updated' % c.update_count
1271
 
        print '%6d files changed too recently to cache' % c.danger_count
1272
 
 
1273
 
        if c.needs_write:
1274
 
            c.write()
1275
 
            
 
1104
        import bzrlib.check
 
1105
        bzrlib.check.check(Branch(dir))
 
1106
 
1276
1107
 
1277
1108
 
1278
1109
class cmd_upgrade(Command):
1285
1116
 
1286
1117
    def run(self, dir='.'):
1287
1118
        from bzrlib.upgrade import upgrade
1288
 
        upgrade(find_branch(dir))
 
1119
        upgrade(Branch(dir))
1289
1120
 
1290
1121
 
1291
1122
 
1303
1134
class cmd_selftest(Command):
1304
1135
    """Run internal test suite"""
1305
1136
    hidden = True
1306
 
    takes_options = ['verbose']
1307
 
    def run(self, verbose=False):
 
1137
    def run(self):
1308
1138
        from bzrlib.selftest import selftest
1309
 
        return int(not selftest(verbose=verbose))
 
1139
        return int(not selftest())
1310
1140
 
1311
1141
 
1312
1142
class cmd_version(Command):
1344
1174
    ['..', -1]
1345
1175
    >>> parse_spec("../f/@35")
1346
1176
    ['../f', 35]
1347
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1348
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1349
1177
    """
1350
1178
    if spec is None:
1351
1179
        return [None, None]
1355
1183
        if parsed[1] == "":
1356
1184
            parsed[1] = -1
1357
1185
        else:
1358
 
            try:
1359
 
                parsed[1] = int(parsed[1])
1360
 
            except ValueError:
1361
 
                pass # We can allow stuff like ./@revid:blahblahblah
1362
 
            else:
1363
 
                assert parsed[1] >=0
 
1186
            parsed[1] = int(parsed[1])
 
1187
            assert parsed[1] >=0
1364
1188
    else:
1365
1189
        parsed = [spec, None]
1366
1190
    return parsed
1398
1222
              check_clean=(not force))
1399
1223
 
1400
1224
 
1401
 
 
1402
1225
class cmd_revert(Command):
1403
 
    """Restore selected files from a previous revision.
1404
 
    """
1405
 
    takes_args = ['file+']
1406
 
    def run(self, file_list):
1407
 
        from bzrlib.branch import find_branch
1408
 
        
1409
 
        if not file_list:
1410
 
            file_list = ['.']
1411
 
            
1412
 
        b = find_branch(file_list[0])
1413
 
 
1414
 
        b.revert([b.relpath(f) for f in file_list])
1415
 
 
1416
 
 
1417
 
class cmd_merge_revert(Command):
1418
1226
    """Reverse all changes since the last commit.
1419
1227
 
1420
1228
    Only versioned files are affected.
1424
1232
    """
1425
1233
    takes_options = ['revision']
1426
1234
 
1427
 
    def run(self, revision=None):
 
1235
    def run(self, revision=-1):
1428
1236
        from bzrlib.merge import merge
1429
 
        if revision is None:
1430
 
            revision = [-1]
1431
 
        elif len(revision) != 1:
1432
 
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1433
 
        merge(('.', revision[0]), parse_spec('.'),
 
1237
        merge(('.', revision), parse_spec('.'),
1434
1238
              check_clean=False,
1435
1239
              ignore_zero=True)
1436
1240
 
1454
1258
        help.help(topic)
1455
1259
 
1456
1260
 
 
1261
class cmd_update_stat_cache(Command):
 
1262
    """Update stat-cache mapping inodes to SHA-1 hashes.
 
1263
 
 
1264
    For testing only."""
 
1265
    hidden = True
 
1266
    def run(self):
 
1267
        import statcache
 
1268
        b = Branch('.')
 
1269
        statcache.update_cache(b.base, b.read_working_inventory())
 
1270
 
1457
1271
 
1458
1272
 
1459
1273
class cmd_plugins(Command):
1461
1275
    hidden = True
1462
1276
    def run(self):
1463
1277
        import bzrlib.plugin
1464
 
        from inspect import getdoc
1465
1278
        from pprint import pprint
1466
 
        for plugin in bzrlib.plugin.all_plugins:
1467
 
            print plugin.__path__[0]
1468
 
            d = getdoc(plugin)
1469
 
            if d:
1470
 
                print '\t', d.split('\n')[0]
1471
 
 
1472
 
        #pprint(bzrlib.plugin.all_plugins)
 
1279
        pprint(bzrlib.plugin.all_plugins)
1473
1280
 
1474
1281
 
1475
1282
 
1493
1300
    'verbose':                None,
1494
1301
    'version':                None,
1495
1302
    'email':                  None,
1496
 
    'unchanged':              None,
1497
1303
    'update':                 None,
1498
 
    'long':                   None,
1499
 
    'root':                   str,
1500
1304
    }
1501
1305
 
1502
1306
SHORT_OPTIONS = {
1505
1309
    'm':                      'message',
1506
1310
    'r':                      'revision',
1507
1311
    'v':                      'verbose',
1508
 
    'l':                      'long',
1509
1312
}
1510
1313
 
1511
1314
 
1526
1329
    >>> parse_args('commit --message=biter'.split())
1527
1330
    (['commit'], {'message': u'biter'})
1528
1331
    >>> parse_args('log -r 500'.split())
1529
 
    (['log'], {'revision': [500]})
1530
 
    >>> parse_args('log -r500..600'.split())
 
1332
    (['log'], {'revision': 500})
 
1333
    >>> parse_args('log -r500:600'.split())
1531
1334
    (['log'], {'revision': [500, 600]})
1532
 
    >>> parse_args('log -vr500..600'.split())
 
1335
    >>> parse_args('log -vr500:600'.split())
1533
1336
    (['log'], {'verbose': True, 'revision': [500, 600]})
1534
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
1535
 
    (['log'], {'revision': ['v500', 600]})
 
1337
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
 
1338
    Traceback (most recent call last):
 
1339
    ...
 
1340
    ValueError: invalid literal for int(): v500
1536
1341
    """
1537
1342
    args = []
1538
1343
    opts = {}
1736
1541
        # some options like --builtin and --no-plugins have special effects
1737
1542
        argv, master_opts = _parse_master_args(argv)
1738
1543
        if not master_opts['no-plugins']:
1739
 
            from bzrlib.plugin import load_plugins
1740
 
            load_plugins()
 
1544
            bzrlib.load_plugins()
1741
1545
 
1742
1546
        args, opts = parse_args(argv)
1743
1547
 
1832
1636
 
1833
1637
 
1834
1638
def main(argv):
 
1639
    import errno
1835
1640
    
1836
 
    bzrlib.trace.open_tracefile(argv)
 
1641
    bzrlib.open_tracefile(argv)
1837
1642
 
1838
1643
    try:
1839
1644
        try:
1860
1665
            _report_exception('interrupted', quiet=True)
1861
1666
            return 2
1862
1667
        except Exception, e:
1863
 
            import errno
1864
1668
            quiet = False
1865
1669
            if (isinstance(e, IOError) 
1866
1670
                and hasattr(e, 'errno')