1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
 
3
# This program is free software; you can redistribute it and/or modify
 
 
4
# it under the terms of the GNU General Public License as published by
 
 
5
# the Free Software Foundation; either version 2 of the License, or
 
 
6
# (at your option) any later version.
 
 
8
# This program is distributed in the hope that it will be useful,
 
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
11
# GNU General Public License for more details.
 
 
13
# You should have received a copy of the GNU General Public License
 
 
14
# along with this program; if not, write to the Free Software
 
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
17
"""Bazaar-NG -- a free distributed version-control tool
 
 
20
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
 
 
22
* Metadata format is not stable yet -- you may need to
 
 
23
  discard history in the future.
 
 
25
* Many commands unimplemented or partially implemented.
 
 
27
* Space-inefficient storage.
 
 
29
* No merge operators yet.
 
 
36
      Show software version/licence/non-warranty.
 
 
38
      Start versioning the current directory
 
 
42
      Show revision history.
 
 
45
  bzr move FROM... DESTDIR
 
 
46
      Move one or more files to a different directory.
 
 
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.
 
 
54
      Show summary of pending changes.
 
 
56
      Make a file not versioned.
 
 
58
      Show statistics about this branch.
 
 
60
      Verify history is stored safely. 
 
 
61
  (for more type 'bzr help commands')
 
 
67
import sys, os, time, types, shutil, tempfile, fnmatch, difflib, os.path
 
 
69
from pprint import pprint
 
 
74
from bzrlib.store import ImmutableStore
 
 
75
from bzrlib.trace import mutter, note, log_error
 
 
76
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
 
 
77
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
 
 
78
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
 
 
79
from bzrlib.revision import Revision
 
 
80
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
 
 
83
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
 
 
84
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
 
 
86
## standard representation
 
 
87
NONE_STRING = '(none)'
 
 
101
def get_cmd_class(cmd):
 
 
104
    cmd = CMD_ALIASES.get(cmd, cmd)
 
 
107
        cmd_class = globals()['cmd_' + cmd.replace('-', '_')]
 
 
109
        raise BzrError("unknown command %r" % cmd)
 
 
111
    return cmd, cmd_class
 
 
116
    """Base class for commands.
 
 
118
    The docstring for an actual command should give a single-line
 
 
119
    summary, then a complete description of the command.  A grammar
 
 
120
    description will be inserted.
 
 
123
        List of argument forms, marked with whether they are optional,
 
 
127
        List of options that may be given for this command.
 
 
130
        If true, this command isn't advertised.
 
 
139
    def __init__(self, options, arguments):
 
 
140
        """Construct and run the command.
 
 
142
        Sets self.status to the return value of run()."""
 
 
143
        assert isinstance(options, dict)
 
 
144
        assert isinstance(arguments, dict)
 
 
145
        cmdargs = options.copy()
 
 
146
        cmdargs.update(arguments)
 
 
147
        assert self.__doc__ != Command.__doc__, \
 
 
148
               ("No help message set for %r" % self)
 
 
149
        self.status = self.run(**cmdargs)
 
 
153
        """Override this in sub-classes.
 
 
155
        This is invoked with the options and arguments bound to
 
 
158
        Return True if the command was successful, False if not.
 
 
164
class cmd_status(Command):
 
 
165
    """Display status summary.
 
 
167
    For each file there is a single line giving its file state and name.
 
 
168
    The name is that in the current revision unless it is deleted or
 
 
169
    missing, in which case the old name is shown.
 
 
171
    takes_options = ['all']
 
 
173
    def run(self, all=False):
 
 
174
        #import bzrlib.status
 
 
175
        #bzrlib.status.tree_status(Branch('.'))
 
 
176
        Branch('.').show_status(show_all=all)
 
 
179
class cmd_cat_revision(Command):
 
 
180
    """Write out metadata for a revision."""
 
 
183
    takes_args = ['revision_id']
 
 
185
    def run(self, revision_id):
 
 
186
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
 
 
189
class cmd_revno(Command):
 
 
190
    """Show current revision number.
 
 
192
    This is equal to the number of revisions on this branch."""
 
 
194
        print Branch('.').revno()
 
 
197
class cmd_add(Command):
 
 
198
    """Add specified files or directories.
 
 
200
    In non-recursive mode, all the named items are added, regardless
 
 
201
    of whether they were previously ignored.  A warning is given if
 
 
202
    any of the named files are already versioned.
 
 
204
    In recursive mode (the default), files are treated the same way
 
 
205
    but the behaviour for directories is different.  Directories that
 
 
206
    are already versioned do not give a warning.  All directories,
 
 
207
    whether already versioned or not, are searched for files or
 
 
208
    subdirectories that are neither versioned or ignored, and these
 
 
209
    are added.  This search proceeds recursively into versioned
 
 
212
    Therefore simply saying 'bzr add .' will version all files that
 
 
213
    are currently unknown.
 
 
215
    TODO: Perhaps adding a file whose directly is not versioned should
 
 
216
    recursively add that parent, rather than giving an error?
 
 
218
    takes_args = ['file+']
 
 
219
    takes_options = ['verbose']
 
 
221
    def run(self, file_list, verbose=False):
 
 
222
        bzrlib.add.smart_add(file_list, verbose)
 
 
225
def Relpath(Command):
 
 
226
    """Show path of a file relative to root"""
 
 
227
    takes_args = ('filename')
 
 
230
        print Branch(self.args['filename']).relpath(filename)
 
 
234
class cmd_inventory(Command):
 
 
235
    """Show inventory of the current working copy or a revision."""
 
 
236
    takes_options = ['revision']
 
 
238
    def run(self, revision=None):
 
 
241
            inv = b.read_working_inventory()
 
 
243
            inv = b.get_revision_inventory(b.lookup_revision(revision))
 
 
245
        for path, entry in inv.iter_entries():
 
 
246
            print '%-50s %s' % (entry.file_id, path)
 
 
249
class cmd_move(Command):
 
 
250
    """Move files to a different directory.
 
 
255
    The destination must be a versioned directory in the same branch.
 
 
257
    takes_args = ['source$', 'dest']
 
 
258
    def run(self, source_list, dest):
 
 
261
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
 
 
264
class cmd_rename(Command):
 
 
265
    """Change the name of an entry.
 
 
268
      bzr rename frob.c frobber.c
 
 
269
      bzr rename src/frob.c lib/frob.c
 
 
271
    It is an error if the destination name exists.
 
 
273
    See also the 'move' command, which moves files into a different
 
 
274
    directory without changing their name.
 
 
276
    TODO: Some way to rename multiple files without invoking bzr for each
 
 
278
    takes_args = ['from_name', 'to_name']
 
 
280
    def run(self, from_name, to_name):
 
 
282
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
 
 
286
class cmd_renames(Command):
 
 
287
    """Show list of renamed files.
 
 
289
    TODO: Option to show renames between two historical versions.
 
 
291
    TODO: Only show renames under dir, rather than in the whole branch.
 
 
293
    takes_args = ['dir?']
 
 
295
    def run(self, dir='.'):
 
 
297
        old_inv = b.basis_tree().inventory
 
 
298
        new_inv = b.read_working_inventory()
 
 
300
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
 
 
302
        for old_name, new_name in renames:
 
 
303
            print "%s => %s" % (old_name, new_name)        
 
 
306
class cmd_info(Command):
 
 
307
    """Show statistical information for this branch"""
 
 
310
        info.show_info(Branch('.'))        
 
 
313
class cmd_remove(Command):
 
 
314
    """Make a file unversioned.
 
 
316
    This makes bzr stop tracking changes to a versioned file.  It does
 
 
317
    not delete the working copy.
 
 
319
    takes_args = ['file+']
 
 
320
    takes_options = ['verbose']
 
 
322
    def run(self, file_list, verbose=False):
 
 
323
        b = Branch(file_list[0])
 
 
324
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
 
 
327
class cmd_file_id(Command):
 
 
328
    """Print file_id of a particular file or directory.
 
 
330
    The file_id is assigned when the file is first added and remains the
 
 
331
    same through all revisions where the file exists, even when it is
 
 
335
    takes_args = ['filename']
 
 
336
    def run(self, filename):
 
 
338
        i = b.inventory.path2id(b.relpath(filename))
 
 
340
            bailout("%r is not a versioned file" % filename)
 
 
345
class cmd_file_path(Command):
 
 
346
    """Print path of file_ids to a file or directory.
 
 
348
    This prints one line for each directory down to the target,
 
 
349
    starting at the branch root."""
 
 
351
    takes_args = ['filename']
 
 
352
    def run(self, filename):
 
 
355
        fid = inv.path2id(b.relpath(filename))
 
 
357
            bailout("%r is not a versioned file" % filename)
 
 
358
        for fip in inv.get_idpath(fid):
 
 
362
class cmd_revision_history(Command):
 
 
363
    """Display list of revision ids on this branch."""
 
 
365
        for patchid in Branch('.').revision_history():
 
 
369
class cmd_directories(Command):
 
 
370
    """Display list of versioned directories in this branch."""
 
 
372
        for name, ie in Branch('.').read_working_inventory().directories():
 
 
379
class cmd_init(Command):
 
 
380
    """Make a directory into a versioned branch.
 
 
382
    Use this to create an empty branch, or before importing an
 
 
385
    Recipe for importing a tree of files:
 
 
390
        bzr commit -m 'imported project'
 
 
393
        Branch('.', init=True)
 
 
396
class cmd_diff(Command):
 
 
397
    """Show differences in working tree.
 
 
399
    If files are listed, only the changes in those files are listed.
 
 
400
    Otherwise, all changes for the tree are listed.
 
 
402
    TODO: Given two revision arguments, show the difference between them.
 
 
404
    TODO: Allow diff across branches.
 
 
406
    TODO: Option to use external diff command; could be GNU diff, wdiff,
 
 
409
    TODO: Python difflib is not exactly the same as unidiff; should
 
 
410
          either fix it up or prefer to use an external diff.
 
 
412
    TODO: If a directory is given, diff everything under that.
 
 
414
    TODO: Selected-file diff is inefficient and doesn't show you
 
 
417
    TODO: This probably handles non-Unix newlines poorly.
 
 
420
    takes_args = ['file*']
 
 
421
    takes_options = ['revision']
 
 
423
    def run(self, revision=None, file_list=None):
 
 
424
        from bzrlib.diff import show_diff
 
 
426
        show_diff(Branch('.'), revision, file_list)
 
 
429
class cmd_deleted(Command):
 
 
430
    """List files deleted in the working tree.
 
 
432
    TODO: Show files deleted since a previous revision, or between two revisions.
 
 
434
    def run(self, show_ids=False):
 
 
437
        new = b.working_tree()
 
 
439
        ## TODO: Much more efficient way to do this: read in new
 
 
440
        ## directories with readdir, rather than stating each one.  Same
 
 
441
        ## level of effort but possibly much less IO.  (Or possibly not,
 
 
442
        ## if the directories are very large...)
 
 
444
        for path, ie in old.inventory.iter_entries():
 
 
445
            if not new.has_id(ie.file_id):
 
 
447
                    print '%-50s %s' % (path, ie.file_id)
 
 
451
class cmd_root(Command):
 
 
452
    """Show the tree root directory.
 
 
454
    The root is the nearest enclosing directory with a .bzr control
 
 
456
    takes_args = ['filename?']
 
 
457
    def run(self, filename=None):
 
 
458
        """Print the branch root."""
 
 
459
        print bzrlib.branch.find_branch_root(filename)
 
 
463
class cmd_log(Command):
 
 
464
    """Show log of this branch.
 
 
466
    TODO: Options to show ids; to limit range; etc.
 
 
468
    takes_options = ['timezone', 'verbose']
 
 
469
    def run(self, timezone='original', verbose=False):
 
 
470
        Branch('.').write_log(show_timezone=timezone, verbose=verbose)
 
 
473
class cmd_ls(Command):
 
 
474
    """List files in a tree.
 
 
476
    TODO: Take a revision or remote path and list that tree instead.
 
 
479
    def run(self, revision=None, verbose=False):
 
 
482
            tree = b.working_tree()
 
 
484
            tree = b.revision_tree(b.lookup_revision(revision))
 
 
486
        for fp, fc, kind, fid in tree.list_files():
 
 
488
                if kind == 'directory':
 
 
495
                print '%-8s %s%s' % (fc, fp, kindch)
 
 
501
class cmd_unknowns(Command):
 
 
502
    """List unknown files"""
 
 
504
        for f in Branch('.').unknowns():
 
 
509
class cmd_ignore(Command):
 
 
510
    """Ignore a command or pattern"""
 
 
511
    takes_args = ['name_pattern']
 
 
513
    def run(self, name_pattern):
 
 
516
        # XXX: This will fail if it's a hardlink; should use an AtomicFile class.
 
 
517
        f = open(b.abspath('.bzrignore'), 'at')
 
 
518
        f.write(name_pattern + '\n')
 
 
521
        inv = b.working_tree().inventory
 
 
522
        if inv.path2id('.bzrignore'):
 
 
523
            mutter('.bzrignore is already versioned')
 
 
525
            mutter('need to make new .bzrignore file versioned')
 
 
526
            b.add(['.bzrignore'])
 
 
530
class cmd_ignored(Command):
 
 
531
    """List ignored files and the patterns that matched them."""
 
 
533
        tree = Branch('.').working_tree()
 
 
534
        for path, file_class, kind, file_id in tree.list_files():
 
 
535
            if file_class != 'I':
 
 
537
            ## XXX: Slightly inefficient since this was already calculated
 
 
538
            pat = tree.is_ignored(path)
 
 
539
            print '%-50s %s' % (path, pat)
 
 
542
class cmd_lookup_revision(Command):
 
 
543
    """Lookup the revision-id from a revision-number
 
 
546
        bzr lookup-revision 33
 
 
549
    def run(self, revno):
 
 
553
            raise BzrError("not a valid revision-number: %r" % revno)
 
 
555
        print Branch('.').lookup_revision(revno) or NONE_STRING
 
 
559
class cmd_export(Command):
 
 
560
    """Export past revision to destination directory.
 
 
562
    If no revision is specified this exports the last committed revision."""
 
 
563
    takes_args = ['dest']
 
 
564
    takes_options = ['revision']
 
 
565
    def run(self, dest, revno=None):
 
 
568
            rh = b.revision_history[-1]
 
 
570
            rh = b.lookup_revision(int(revno))
 
 
571
        t = b.revision_tree(rh)
 
 
575
class cmd_cat(Command):
 
 
576
    """Write a file's text from a previous revision."""
 
 
578
    takes_options = ['revision']
 
 
579
    takes_args = ['filename']
 
 
581
    def run(self, filename, revision=None):
 
 
583
            raise BzrCommandError("bzr cat requires a revision number")
 
 
585
        b.print_file(b.relpath(filename), int(revision))
 
 
588
class cmd_local_time_offset(Command):
 
 
589
    """Show the offset in seconds from GMT to local time."""
 
 
592
        print bzrlib.osutils.local_time_offset()
 
 
596
class cmd_commit(Command):
 
 
597
    """Commit changes into a new revision.
 
 
599
    TODO: Commit only selected files.
 
 
601
    TODO: Run hooks on tree to-be-committed, and after commit.
 
 
603
    TODO: Strict commit that fails if there are unknown or deleted files.
 
 
605
    takes_options = ['message', 'verbose']
 
 
607
    def run(self, message=None, verbose=False):
 
 
609
            raise BzrCommandError("please specify a commit message")
 
 
610
        Branch('.').commit(message, verbose=verbose)
 
 
613
class cmd_check(Command):
 
 
614
    """Validate consistency of branch history.
 
 
616
    This command checks various invariants about the branch storage to
 
 
617
    detect data corruption or bzr bugs.
 
 
619
    takes_args = ['dir?']
 
 
620
    def run(self, dir='.'):
 
 
622
        bzrlib.check.check(Branch(dir, find_root=False))
 
 
626
class cmd_whoami(Command):
 
 
627
    """Show bzr user id."""
 
 
628
    takes_options = ['email']
 
 
630
    def run(self, email=False):
 
 
632
            print bzrlib.osutils.user_email()
 
 
634
            print bzrlib.osutils.username()
 
 
637
class cmd_selftest(Command):
 
 
638
    """Run internal test suite"""
 
 
641
        failures, tests = 0, 0
 
 
643
        import doctest, bzrlib.store, bzrlib.tests
 
 
644
        bzrlib.trace.verbose = False
 
 
646
        for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
 
 
647
            bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
 
 
648
            mf, mt = doctest.testmod(m)
 
 
651
            print '%-40s %3d tests' % (m.__name__, mt),
 
 
653
                print '%3d FAILED!' % mf
 
 
657
        print '%-40s %3d tests' % ('total', tests),
 
 
659
            print '%3d FAILED!' % failures
 
 
665
class cmd_version(Command):
 
 
666
    """Show version of bzr"""
 
 
671
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
 
 
672
    print bzrlib.__copyright__
 
 
673
    print "http://bazaar-ng.org/"
 
 
675
    print "bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and"
 
 
676
    print "you may use, modify and redistribute it under the terms of the GNU"
 
 
677
    print "General Public License version 2 or later."
 
 
680
class cmd_rocks(Command):
 
 
681
    """Statement of optimism."""
 
 
684
        print "it sure does!"
 
 
687
class cmd_assert_fail(Command):
 
 
688
    """Test reporting of assertion failures"""
 
 
691
        assert False, "always fails"
 
 
694
class cmd_help(Command):
 
 
695
    """Show help on a command or other topic.
 
 
697
    For a list of all available commands, say 'bzr help commands'."""
 
 
698
    takes_args = ['topic?']
 
 
700
    def run(self, topic=None):
 
 
704
def help(topic=None):
 
 
707
    elif topic == 'commands':
 
 
710
        help_on_command(topic)
 
 
713
def help_on_command(cmdname):
 
 
714
    cmdname = str(cmdname)
 
 
716
    from inspect import getdoc
 
 
717
    topic, cmdclass = get_cmd_class(cmdname)
 
 
719
    doc = getdoc(cmdclass)
 
 
721
        raise NotImplementedError("sorry, no detailed help yet for %r" % cmdname)
 
 
724
        short, rest = doc.split('\n', 1)
 
 
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] + '...]'
 
 
744
    if cmdclass.takes_options:
 
 
747
        for on in cmdclass.takes_options:
 
 
752
    """List all commands"""
 
 
756
    for k, v in globals().items():
 
 
757
        if k.startswith('cmd_'):
 
 
758
            accu.append((k[4:].replace('_','-'), v))
 
 
760
    for cmdname, cmdclass in accu:
 
 
764
        help = inspect.getdoc(cmdclass)
 
 
766
            print "    " + help.split('\n', 1)[0]
 
 
769
######################################################################
 
 
773
# list of all available options; the rhs can be either None for an
 
 
774
# option that takes no argument, or a constructor function that checks
 
 
796
def parse_args(argv):
 
 
797
    """Parse command line.
 
 
799
    Arguments and options are parsed at this level before being passed
 
 
800
    down to specific command handlers.  This routine knows, from a
 
 
801
    lookup table, something about the available options, what optargs
 
 
802
    they take, and which commands will accept them.
 
 
804
    >>> parse_args('--help'.split())
 
 
806
    >>> parse_args('--version'.split())
 
 
807
    ([], {'version': True})
 
 
808
    >>> parse_args('status --all'.split())
 
 
809
    (['status'], {'all': True})
 
 
810
    >>> parse_args('commit --message=biter'.split())
 
 
811
    (['commit'], {'message': u'biter'})
 
 
816
    # TODO: Maybe handle '--' to end options?
 
 
821
            # option names must not be unicode
 
 
825
                mutter("  got option %r" % a)
 
 
827
                    optname, optarg = a[2:].split('=', 1)
 
 
830
                if optname not in OPTIONS:
 
 
831
                    bailout('unknown long option %r' % a)
 
 
834
                if shortopt not in SHORT_OPTIONS:
 
 
835
                    bailout('unknown short option %r' % a)
 
 
836
                optname = SHORT_OPTIONS[shortopt]
 
 
839
                # XXX: Do we ever want to support this, e.g. for -r?
 
 
840
                bailout('repeated option %r' % a)
 
 
842
            optargfn = OPTIONS[optname]
 
 
846
                        bailout('option %r needs an argument' % a)
 
 
849
                opts[optname] = optargfn(optarg)
 
 
852
                    bailout('option %r takes no argument' % optname)
 
 
862
def _match_argform(cmd, takes_args, args):
 
 
865
    # step through args and takes_args, allowing appropriate 0-many matches
 
 
866
    for ap in takes_args:
 
 
870
                argdict[argname] = args.pop(0)
 
 
871
        elif ap[-1] == '*': # all remaining arguments
 
 
873
                argdict[argname + '_list'] = args[:]
 
 
876
                argdict[argname + '_list'] = None
 
 
879
                raise BzrCommandError("command %r needs one or more %s"
 
 
880
                        % (cmd, argname.upper()))
 
 
882
                argdict[argname + '_list'] = args[:]
 
 
884
        elif ap[-1] == '$': # all but one
 
 
886
                raise BzrCommandError("command %r needs one or more %s"
 
 
887
                        % (cmd, argname.upper()))
 
 
888
            argdict[argname + '_list'] = args[:-1]
 
 
894
                raise BzrCommandError("command %r requires argument %s"
 
 
895
                        % (cmd, argname.upper()))
 
 
897
                argdict[argname] = args.pop(0)
 
 
900
        raise BzrCommandError("extra argument to command %s: %s"
 
 
908
    """Execute a command.
 
 
910
    This is similar to main(), but without all the trappings for
 
 
911
    logging and error handling.  
 
 
914
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
 
917
        args, opts = parse_args(argv[1:])
 
 
924
        elif 'version' in opts:
 
 
927
        cmd = str(args.pop(0))
 
 
929
        log_error('usage: bzr COMMAND')
 
 
930
        log_error('  try "bzr help"')
 
 
933
    canonical_cmd, cmd_class = get_cmd_class(cmd)
 
 
936
    if 'profile' in opts:
 
 
942
    # check options are reasonable
 
 
943
    allowed = cmd_class.takes_options
 
 
945
        if oname not in allowed:
 
 
946
            raise BzrCommandError("option %r is not allowed for command %r"
 
 
949
    # mix arguments and options into one dictionary
 
 
950
    cmdargs = _match_argform(cmd, cmd_class.takes_args, args)
 
 
952
    for k, v in opts.items():
 
 
953
        cmdopts[k.replace('-', '_')] = v
 
 
957
        pffileno, pfname = tempfile.mkstemp()
 
 
959
            prof = hotshot.Profile(pfname)
 
 
960
            ret = prof.runcall(cmd_class, cmdopts, cmdargs) or 0
 
 
964
            stats = hotshot.stats.load(pfname)
 
 
966
            stats.sort_stats('time')
 
 
967
            ## XXX: Might like to write to stderr or the trace file instead but
 
 
968
            ## print_stats seems hardcoded to stdout
 
 
969
            stats.print_stats(20)
 
 
977
        cmdobj = cmd_class(cmdopts, cmdargs) or 0
 
 
981
def _report_exception(e, summary, quiet=False):
 
 
983
    log_error('bzr: ' + summary)
 
 
984
    bzrlib.trace.log_exception(e)
 
 
987
        tb = sys.exc_info()[2]
 
 
988
        exinfo = traceback.extract_tb(tb)
 
 
990
            sys.stderr.write('  at %s:%d in %s()\n' % exinfo[-1][:3])
 
 
991
        sys.stderr.write('  see ~/.bzr.log for debug information\n')
 
 
998
    bzrlib.trace.create_tracefile(argv)
 
 
1003
            # do this here to catch EPIPE
 
 
1007
            quiet = isinstance(e, (BzrCommandError))
 
 
1008
            _report_exception(e, 'error: ' + e.args[0], quiet=quiet)
 
 
1011
                    # some explanation or hints
 
 
1014
        except AssertionError, e:
 
 
1015
            msg = 'assertion failed'
 
 
1017
                msg += ': ' + str(e)
 
 
1018
            _report_exception(e, msg)
 
 
1020
        except KeyboardInterrupt, e:
 
 
1021
            _report_exception(e, 'interrupted', quiet=True)
 
 
1023
        except Exception, e:
 
 
1025
            if isinstance(e, IOError) and e.errno == errno.EPIPE:
 
 
1029
                msg = str(e).rstrip('\n')
 
 
1030
            _report_exception(e, msg, quiet)
 
 
1033
        bzrlib.trace.close_trace()
 
 
1036
if __name__ == '__main__':
 
 
1037
    sys.exit(main(sys.argv))