~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-05-09 03:03:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050509030355-ad6ab558d1362959
- Don't give an error if the trace file can't be opened

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
"""Bazaar-NG -- a free distributed version-control tool
18
 
http://bazaar-ng.org/
19
 
 
20
 
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
21
 
 
22
 
* Metadata format is not stable yet -- you may need to
23
 
  discard history in the future.
24
 
 
25
 
* Many commands unimplemented or partially implemented.
26
 
 
27
 
* Space-inefficient storage.
28
 
 
29
 
* No merge operators yet.
30
 
 
31
 
Interesting commands:
32
 
 
33
 
  bzr help [COMMAND]
34
 
      Show help screen
35
 
  bzr version
36
 
      Show software version/licence/non-warranty.
37
 
  bzr init
38
 
      Start versioning the current directory
39
 
  bzr add FILE...
40
 
      Make files versioned.
41
 
  bzr log
42
 
      Show revision history.
43
 
  bzr rename FROM TO
44
 
      Rename one file.
45
 
  bzr move FROM... DESTDIR
46
 
      Move one or more files to a different directory.
47
 
  bzr diff [FILE...]
48
 
      Show changes from last revision to working copy.
49
 
  bzr commit -m 'MESSAGE'
50
 
      Store current state as new revision.
51
 
  bzr export [-r REVNO] DESTINATION
52
 
      Export the branch state at a previous version.
53
 
  bzr status
54
 
      Show summary of pending changes.
55
 
  bzr remove FILE...
56
 
      Make a file not versioned.
57
 
  bzr info
58
 
      Show statistics about this branch.
59
 
  bzr check
60
 
      Verify history is stored safely. 
61
 
  (for more type 'bzr help commands')
62
 
"""
63
 
 
64
 
 
65
 
 
66
 
 
67
 
import sys, os, time, types, shutil, tempfile, fnmatch, difflib, os.path
 
17
 
 
18
 
 
19
import sys, os, time, os.path
68
20
from sets import Set
69
 
from pprint import pprint
70
 
from stat import *
71
 
from glob import glob
72
21
 
73
22
import bzrlib
74
 
from bzrlib.store import ImmutableStore
75
23
from bzrlib.trace import mutter, note, log_error
76
24
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
77
25
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
80
28
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
81
29
     format_date
82
30
 
83
 
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
84
 
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
85
 
 
86
 
## standard representation
87
 
NONE_STRING = '(none)'
88
 
EMPTY = 'empty'
89
 
 
90
 
 
91
 
CMD_ALIASES = {
92
 
    '?':         'help',
93
 
    'ci':        'commit',
94
 
    'checkin':   'commit',
95
 
    'di':        'diff',
96
 
    'st':        'status',
97
 
    'stat':      'status',
98
 
    }
99
 
 
 
31
 
 
32
def _squish_command_name(cmd):
 
33
    return 'cmd_' + cmd.replace('-', '_')
 
34
 
 
35
 
 
36
def _unsquish_command_name(cmd):
 
37
    assert cmd.startswith("cmd_")
 
38
    return cmd[4:].replace('_','-')
 
39
 
 
40
def get_all_cmds():
 
41
    """Return canonical name and class for all registered commands."""
 
42
    for k, v in globals().iteritems():
 
43
        if k.startswith("cmd_"):
 
44
            yield _unsquish_command_name(k), v
100
45
 
101
46
def get_cmd_class(cmd):
102
 
    cmd = str(cmd)
103
 
    
104
 
    cmd = CMD_ALIASES.get(cmd, cmd)
105
 
    
 
47
    """Return the canonical name and command class for a command.
 
48
    """
 
49
    cmd = str(cmd)                      # not unicode
 
50
 
 
51
    # first look up this command under the specified name
106
52
    try:
107
 
        cmd_class = globals()['cmd_' + cmd.replace('-', '_')]
 
53
        return cmd, globals()[_squish_command_name(cmd)]
108
54
    except KeyError:
109
 
        raise BzrError("unknown command %r" % cmd)
110
 
 
111
 
    return cmd, cmd_class
112
 
 
 
55
        pass
 
56
 
 
57
    # look for any command which claims this as an alias
 
58
    for cmdname, cmdclass in get_all_cmds():
 
59
        if cmd in cmdclass.aliases:
 
60
            return cmdname, cmdclass
 
61
    else:
 
62
        raise BzrCommandError("unknown command %r" % cmd)
113
63
 
114
64
 
115
65
class Command:
155
105
        This is invoked with the options and arguments bound to
156
106
        keyword parameters.
157
107
 
158
 
        Return True if the command was successful, False if not.
 
108
        Return 0 or None if the command was successful, or a shell
 
109
        error code if not.
159
110
        """
160
 
        return True
 
111
        return 0
161
112
 
162
113
 
163
114
 
169
120
    missing, in which case the old name is shown.
170
121
    """
171
122
    takes_options = ['all']
 
123
    aliases = ['st', 'stat']
172
124
    
173
125
    def run(self, all=False):
174
126
        #import bzrlib.status
222
174
        bzrlib.add.smart_add(file_list, verbose)
223
175
 
224
176
 
225
 
def Relpath(Command):
 
177
class cmd_relpath(Command):
226
178
    """Show path of a file relative to root"""
227
 
    takes_args = ('filename')
 
179
    takes_args = ['filename']
228
180
    
229
 
    def run(self):
230
 
        print Branch(self.args['filename']).relpath(filename)
 
181
    def run(self, filename):
 
182
        print Branch(filename).relpath(filename)
231
183
 
232
184
 
233
185
 
419
371
    
420
372
    takes_args = ['file*']
421
373
    takes_options = ['revision']
 
374
    aliases = ['di']
422
375
 
423
376
    def run(self, revision=None, file_list=None):
424
377
        from bzrlib.diff import show_diff
463
416
class cmd_log(Command):
464
417
    """Show log of this branch.
465
418
 
466
 
    TODO: Options to show ids; to limit range; etc.
 
419
    TODO: Option to limit range.
 
420
 
 
421
    TODO: Perhaps show most-recent first with an option for last.
467
422
    """
468
 
    takes_options = ['timezone', 'verbose']
469
 
    def run(self, timezone='original', verbose=False):
470
 
        Branch('.').write_log(show_timezone=timezone, verbose=verbose)
 
423
    takes_args = ['filename?']
 
424
    takes_options = ['timezone', 'verbose', 'show-ids']
 
425
    def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
 
426
        b = Branch((filename or '.'), lock_mode='r')
 
427
        if filename:
 
428
            filename = b.relpath(filename)
 
429
        bzrlib.show_log(b, filename,
 
430
                        show_timezone=timezone,
 
431
                        verbose=verbose,
 
432
                        show_ids=show_ids)
 
433
 
 
434
 
 
435
 
 
436
class cmd_touching_revisions(Command):
 
437
    """Return revision-ids which affected a particular file."""
 
438
    hidden = True
 
439
    takes_args = ["filename"]
 
440
    def run(self, filename):
 
441
        b = Branch(filename, lock_mode='r')
 
442
        inv = b.read_working_inventory()
 
443
        file_id = inv.path2id(b.relpath(filename))
 
444
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
 
445
            print "%6d %s" % (revno, what)
471
446
 
472
447
 
473
448
class cmd_ls(Command):
546
521
        bzr lookup-revision 33
547
522
        """
548
523
    hidden = True
 
524
    takes_args = ['revno']
 
525
    
549
526
    def run(self, revno):
550
527
        try:
551
528
            revno = int(revno)
552
529
        except ValueError:
553
 
            raise BzrError("not a valid revision-number: %r" % revno)
554
 
 
555
 
        print Branch('.').lookup_revision(revno) or NONE_STRING
556
 
 
 
530
            raise BzrCommandError("not a valid revision-number: %r" % revno)
 
531
 
 
532
        print Branch('.').lookup_revision(revno)
557
533
 
558
534
 
559
535
class cmd_export(Command):
562
538
    If no revision is specified this exports the last committed revision."""
563
539
    takes_args = ['dest']
564
540
    takes_options = ['revision']
565
 
    def run(self, dest, revno=None):
 
541
    def run(self, dest, revision=None):
566
542
        b = Branch('.')
567
 
        if revno == None:
568
 
            rh = b.revision_history[-1]
 
543
        if revision == None:
 
544
            rh = b.revision_history()[-1]
569
545
        else:
570
 
            rh = b.lookup_revision(int(revno))
 
546
            rh = b.lookup_revision(int(revision))
571
547
        t = b.revision_tree(rh)
572
548
        t.export(dest)
573
549
 
602
578
 
603
579
    TODO: Strict commit that fails if there are unknown or deleted files.
604
580
    """
605
 
    takes_options = ['message', 'verbose']
606
 
    
607
 
    def run(self, message=None, verbose=False):
608
 
        if not message:
609
 
            raise BzrCommandError("please specify a commit message")
 
581
    takes_options = ['message', 'file', 'verbose']
 
582
    aliases = ['ci', 'checkin']
 
583
 
 
584
    def run(self, message=None, file=None, verbose=False):
 
585
        ## Warning: shadows builtin file()
 
586
        if not message and not file:
 
587
            raise BzrCommandError("please specify a commit message",
 
588
                                  ["use either --message or --file"])
 
589
        elif message and file:
 
590
            raise BzrCommandError("please specify either --message or --file")
 
591
        
 
592
        if file:
 
593
            import codecs
 
594
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
 
595
 
610
596
        Branch('.').commit(message, verbose=verbose)
611
597
 
612
598
 
696
682
 
697
683
    For a list of all available commands, say 'bzr help commands'."""
698
684
    takes_args = ['topic?']
 
685
    aliases = ['?']
699
686
    
700
687
    def run(self, topic=None):
701
 
        help(topic)
702
 
 
703
 
 
704
 
def help(topic=None):
705
 
    if topic == None:
706
 
        print __doc__
707
 
    elif topic == 'commands':
708
 
        help_commands()
709
 
    else:
710
 
        help_on_command(topic)
711
 
 
712
 
 
713
 
def help_on_command(cmdname):
714
 
    cmdname = str(cmdname)
715
 
 
716
 
    from inspect import getdoc
717
 
    topic, cmdclass = get_cmd_class(cmdname)
718
 
 
719
 
    doc = getdoc(cmdclass)
720
 
    if doc == None:
721
 
        raise NotImplementedError("sorry, no detailed help yet for %r" % cmdname)
722
 
 
723
 
    if '\n' in doc:
724
 
        short, rest = doc.split('\n', 1)
725
 
    else:
726
 
        short = doc
727
 
        rest = ''
728
 
 
729
 
    print 'usage: bzr ' + topic,
730
 
    for aname in cmdclass.takes_args:
731
 
        aname = aname.upper()
732
 
        if aname[-1] in ['$', '+']:
733
 
            aname = aname[:-1] + '...'
734
 
        elif aname[-1] == '?':
735
 
            aname = '[' + aname[:-1] + ']'
736
 
        elif aname[-1] == '*':
737
 
            aname = '[' + aname[:-1] + '...]'
738
 
        print aname,
739
 
    print 
740
 
    print short
741
 
    if rest:
742
 
        print rest
743
 
 
744
 
    help_on_option(cmdclass.takes_options)
745
 
 
746
 
 
747
 
def help_on_option(options):
748
 
    if not options:
749
 
        return
750
 
    
751
 
    print
752
 
    print 'options:'
753
 
    for on in options:
754
 
        l = '    --' + on
755
 
        for shortname, longname in SHORT_OPTIONS.items():
756
 
            if longname == on:
757
 
                l += ', -' + shortname
758
 
                break
759
 
        print l
760
 
 
761
 
 
762
 
def help_commands():
763
 
    """List all commands"""
764
 
    import inspect
765
 
    
766
 
    accu = []
767
 
    for k, v in globals().items():
768
 
        if k.startswith('cmd_'):
769
 
            accu.append((k[4:].replace('_','-'), v))
770
 
    accu.sort()
771
 
    for cmdname, cmdclass in accu:
772
 
        if cmdclass.hidden:
773
 
            continue
774
 
        print cmdname
775
 
        help = inspect.getdoc(cmdclass)
776
 
        if help:
777
 
            print "    " + help.split('\n', 1)[0]
778
 
            
 
688
        import help
 
689
        help.help(topic)
 
690
 
779
691
 
780
692
######################################################################
781
693
# main routine
787
699
OPTIONS = {
788
700
    'all':                    None,
789
701
    'help':                   None,
 
702
    'file':                   unicode,
790
703
    'message':                unicode,
791
704
    'profile':                None,
792
705
    'revision':               int,
799
712
 
800
713
SHORT_OPTIONS = {
801
714
    'm':                      'message',
 
715
    'F':                      'file', 
802
716
    'r':                      'revision',
803
717
    'v':                      'verbose',
804
718
}
921
835
    This is similar to main(), but without all the trappings for
922
836
    logging and error handling.  
923
837
    """
924
 
 
925
838
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
926
839
    
927
840
    try:
928
841
        args, opts = parse_args(argv[1:])
929
842
        if 'help' in opts:
 
843
            import help
930
844
            if args:
931
 
                help(args[0])
 
845
                help.help(args[0])
932
846
            else:
933
 
                help()
 
847
                help.help()
934
848
            return 0
935
849
        elif 'version' in opts:
936
 
            cmd_version([], [])
 
850
            show_version()
937
851
            return 0
938
852
        cmd = str(args.pop(0))
939
853
    except IndexError:
954
868
    allowed = cmd_class.takes_options
955
869
    for oname in opts:
956
870
        if oname not in allowed:
957
 
            raise BzrCommandError("option %r is not allowed for command %r"
 
871
            raise BzrCommandError("option '--%s' is not allowed for command %r"
958
872
                                  % (oname, cmd))
959
873
 
960
874
    # mix arguments and options into one dictionary
964
878
        cmdopts[k.replace('-', '_')] = v
965
879
 
966
880
    if profile:
967
 
        import hotshot
 
881
        import hotshot, tempfile
968
882
        pffileno, pfname = tempfile.mkstemp()
969
883
        try:
970
884
            prof = hotshot.Profile(pfname)
979
893
            ## print_stats seems hardcoded to stdout
980
894
            stats.print_stats(20)
981
895
            
982
 
            return ret
 
896
            return ret.status
983
897
 
984
898
        finally:
985
899
            os.close(pffileno)
986
900
            os.remove(pfname)
987
901
    else:
988
 
        cmdobj = cmd_class(cmdopts, cmdargs) or 0
989
 
 
990
 
 
991
 
 
992
 
def _report_exception(e, summary, quiet=False):
 
902
        cmdobj = cmd_class(cmdopts, cmdargs).status 
 
903
 
 
904
 
 
905
def _report_exception(summary, quiet=False):
993
906
    import traceback
994
907
    log_error('bzr: ' + summary)
995
 
    bzrlib.trace.log_exception(e)
 
908
    bzrlib.trace.log_exception()
996
909
 
997
910
    if not quiet:
998
911
        tb = sys.exc_info()[2]
1006
919
def main(argv):
1007
920
    import errno
1008
921
    
1009
 
    bzrlib.trace.create_tracefile(argv)
 
922
    bzrlib.open_tracefile(argv)
1010
923
 
1011
924
    try:
1012
925
        try:
1013
 
            ret = run_bzr(argv)
1014
 
            # do this here to catch EPIPE
1015
 
            sys.stdout.flush()
1016
 
            return ret
 
926
            try:
 
927
                return run_bzr(argv)
 
928
            finally:
 
929
                # do this here inside the exception wrappers to catch EPIPE
 
930
                sys.stdout.flush()
1017
931
        except BzrError, e:
1018
932
            quiet = isinstance(e, (BzrCommandError))
1019
 
            _report_exception(e, 'error: ' + e.args[0], quiet=quiet)
 
933
            _report_exception('error: ' + e.args[0], quiet=quiet)
1020
934
            if len(e.args) > 1:
1021
935
                for h in e.args[1]:
1022
936
                    # some explanation or hints
1026
940
            msg = 'assertion failed'
1027
941
            if str(e):
1028
942
                msg += ': ' + str(e)
1029
 
            _report_exception(e, msg)
 
943
            _report_exception(msg)
1030
944
            return 2
1031
945
        except KeyboardInterrupt, e:
1032
 
            _report_exception(e, 'interrupted', quiet=True)
 
946
            _report_exception('interrupted', quiet=True)
1033
947
            return 2
1034
948
        except Exception, e:
1035
949
            quiet = False
1038
952
                msg = 'broken pipe'
1039
953
            else:
1040
954
                msg = str(e).rstrip('\n')
1041
 
            _report_exception(e, msg, quiet)
 
955
            _report_exception(msg, quiet)
1042
956
            return 2
1043
957
    finally:
1044
958
        bzrlib.trace.close_trace()