/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1 by mbp at sourcefrog
import from baz patch-364
1
#! /usr/bin/python
2
3
4
# Copyright (C) 2004, 2005 by Martin Pool
5
# Copyright (C) 2005 by Canonical Ltd
6
7
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
12
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU General Public License for more details.
17
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22
"""Bazaar-NG -- a free distributed version-control tool
23
24
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
25
26
Current limitation include:
27
28
* Metadata format is not stable yet -- you may need to
29
  discard history in the future.
30
31
* No handling of subdirectories, symlinks or any non-text files.
32
33
* Insufficient error handling.
34
35
* Many commands unimplemented or partially implemented.
36
37
* Space-inefficient storage.
38
39
* No merge operators yet.
40
41
Interesting commands::
42
43
  bzr help
44
       Show summary help screen
45
  bzr version
46
       Show software version/licence/non-warranty.
47
  bzr init
48
       Start versioning the current directory
49
  bzr add FILE...
50
       Make files versioned.
51
  bzr log
52
       Show revision history.
53
  bzr diff
54
       Show changes from last revision to working copy.
55
  bzr commit -m 'MESSAGE'
56
       Store current state as new revision.
57
  bzr export REVNO DESTINATION
58
       Export the branch state at a previous version.
59
  bzr status
60
       Show summary of pending changes.
61
  bzr remove FILE...
62
       Make a file not versioned.
63
"""
64
65
# not currently working:
66
#  bzr check
67
#       Run internal consistency checks.
68
#  bzr info
69
#       Show some information about this branch.
70
71
72
73
__copyright__ = "Copyright 2005 Canonical Development Ltd."
74
__author__ = "Martin Pool <mbp@canonical.com>"
75
__docformat__ = "restructuredtext en"
76
__version__ = '0.0.0'
77
78
79
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
80
import traceback, socket, fnmatch, difflib
81
from os import path
82
from sets import Set
83
from pprint import pprint
84
from stat import *
85
from glob import glob
86
87
import bzrlib
88
from bzrlib.store import ImmutableStore
89
from bzrlib.trace import mutter, note, log_error
90
from bzrlib.errors import bailout, BzrError
91
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
92
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
93
from bzrlib.revision import Revision
94
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
95
     format_date
96
97
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
98
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
99
100
## standard representation
101
NONE_STRING = '(none)'
102
EMPTY = 'empty'
103
104
105
## TODO: Perhaps a different version of inventory commands that
106
## returns iterators...
107
108
## TODO: Perhaps an AtomicFile class that writes to a temporary file and then renames.
109
110
## TODO: Some kind of locking on branches.  Perhaps there should be a
111
## parameter to the branch object saying whether we want a read or
112
## write lock; release it from destructor.  Perhaps don't even need a
113
## read lock to look at immutable objects?
114
115
## TODO: Perhaps make UUIDs predictable in test mode to make it easier
116
## to compare output?
117
118
119
120
121
######################################################################
122
# check status
123
124
125
def cmd_status(all=False):
126
    """Display status summary.
127
128
    For each file there is a single line giving its file state and name.
129
    The name is that in the current revision unless it is deleted or
130
    missing, in which case the old name is shown.
131
132
    :todo: Don't show unchanged files unless ``--all`` is given?
133
    """
134
    Branch('.').show_status(show_all=all)
135
136
137
138
######################################################################
139
# examining history
140
def cmd_get_revision(revision_id):
141
    Branch('.').get_revision(revision_id).write_xml(sys.stdout)
142
143
144
def cmd_get_inventory(inventory_id):
145
    """Return inventory in XML by hash"""
146
    Branch('.').get_inventory(inventory_hash).write_xml(sys.stdout)
147
148
149
def cmd_get_revision_inventory(revision_id):
150
    """Output inventory for a revision."""
151
    b = Branch('.')
152
    b.get_revision_inventory(revision_id).write_xml(sys.stdout)
153
154
155
def cmd_get_file_text(text_id):
156
    """Get contents of a file by hash."""
157
    sf = Branch('.').text_store[text_id]
158
    pumpfile(sf, sys.stdout)
159
160
161
162
######################################################################
163
# commands
164
    
165
166
def cmd_revno():
167
    """Show number of revisions on this branch"""
168
    print Branch('.').revno()
169
    
170
171
def cmd_add(file_list, verbose=False):
172
    """Add specified files.
173
    
174
    Fails if the files are already added.
175
    """
176
    Branch('.').add(file_list, verbose=verbose)
177
178
179
def cmd_inventory(revision=None):
180
    """Show inventory of the current working copy."""
181
    ## TODO: Also optionally show a previous inventory
182
    ## TODO: Format options
183
    b = Branch('.')
184
    if revision == None:
185
        inv = b.read_working_inventory()
186
    else:
187
        inv = b.get_revision_inventory(b.lookup_revision(revision))
188
        
189
    for path, entry in inv.iter_entries():
190
        print '%-50s %s' % (entry.file_id, path)
191
192
193
194
def cmd_info():
195
    b = Branch('.')
196
    print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
21 by mbp at sourcefrog
- bzr info: show summary information on branch history
197
198
    def plural(n, base='', pl=None):
199
        if n == 1:
200
            return base
201
        elif pl is not None:
202
            return pl
203
        else:
204
            return 's'
18 by mbp at sourcefrog
show count of versioned/unknown/ignored files
205
206
    count_version_dirs = 0
19 by mbp at sourcefrog
more information in info command
207
208
    count_status = {'A': 0, 'D': 0, 'M': 0, 'R': 0, '?': 0, 'I': 0, '.': 0}
209
    for st_tup in bzrlib.diff_trees(b.basis_tree(), b.working_tree()):
210
        fs = st_tup[0]
211
        count_status[fs] += 1
212
        if fs not in ['I', '?'] and st_tup[4] == 'directory':
213
            count_version_dirs += 1
214
215
    print
216
    print 'in the working tree:'
217
    for name, fs in (('unchanged', '.'),
218
                     ('modified', 'M'), ('added', 'A'), ('removed', 'D'),
219
                     ('renamed', 'R'), ('unknown', '?'), ('ignored', 'I'),
220
                     ):
221
        print '  %5d %s' % (count_status[fs], name)
21 by mbp at sourcefrog
- bzr info: show summary information on branch history
222
    print '  %5d versioned subdirector%s' % (count_version_dirs,
223
                                             plural(count_version_dirs, 'y', 'ies'))
224
225
    print
226
    print 'branch history:'
227
    history = b.revision_history()
228
    revno = len(history)
229
    print '  %5d revision%s' % (revno, plural(revno))
230
    committers = Set()
231
    for rev in history:
232
        committers.add(b.get_revision(rev).committer)
233
    print '  %5d committer%s' % (len(committers), plural(len(committers)))
234
    if revno > 0:
235
        firstrev = b.get_revision(history[0])
236
        age = int((time.time() - firstrev.timestamp) / 3600 / 24)
237
        print '  %5d day%s old' % (age, plural(age))
238
    
1 by mbp at sourcefrog
import from baz patch-364
239
240
241
def cmd_remove(file_list, verbose=False):
242
    Branch('.').remove(file_list, verbose=verbose)
243
244
245
246
def cmd_file_id(filename):
247
    i = Branch('.').read_working_inventory().path2id(filename)
248
    if i is None:
249
        bailout("%s is not a versioned file" % filename)
250
    else:
251
        print i
252
253
254
def cmd_find_filename(fileid):
255
    n = find_filename(fileid)
256
    if n is None:
257
        bailout("%s is not a live file id" % fileid)
258
    else:
259
        print n
260
261
262
def cmd_revision_history():
263
    for patchid in Branch('.').revision_history():
264
        print patchid
265
266
267
268
def cmd_init():
269
    # TODO: Check we're not already in a working directory?  At the
270
    # moment you'll get an ugly error.
271
    
272
    # TODO: What if we're in a subdirectory of a branch?  Would like
273
    # to allow that, but then the parent may need to understand that
274
    # the children have disappeared, or should they be versioned in
275
    # both?
276
277
    # TODO: Take an argument/option for branch name.
278
    Branch('.', init=True)
279
280
281
def cmd_diff(revision=None):
282
    """Show diff from basis to working copy.
283
284
    :todo: Take one or two revision arguments, look up those trees,
285
           and diff them.
286
287
    :todo: Allow diff across branches.
288
289
    :todo: Mangle filenames in diff to be more relevant.
290
291
    :todo: Shouldn't be in the cmd function.
292
    """
293
294
    b = Branch('.')
295
296
    if revision == None:
297
        old_tree = b.basis_tree()
298
    else:
299
        old_tree = b.revision_tree(b.lookup_revision(revision))
300
        
301
    new_tree = b.working_tree()
302
    old_inv = old_tree.inventory
303
    new_inv = new_tree.inventory
304
305
    # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
306
    old_label = ''
307
    new_label = ''
308
309
    DEVNULL = '/dev/null'
310
    # Windows users, don't panic about this filename -- it is a
311
    # special signal to GNU patch that the file should be created or
312
    # deleted respectively.
313
314
    # TODO: Generation of pseudo-diffs for added/deleted files could
315
    # be usefully made into a much faster special case.
316
317
    # TODO: Better to return them in sorted order I think.
318
    
319
    for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
320
        d = None
321
322
        # Don't show this by default; maybe do it if an option is passed
323
        # idlabel = '      {%s}' % fid
324
        idlabel = ''
325
326
        # FIXME: Something about the diff format makes patch unhappy
327
        # with newly-added files.
328
329
        def diffit(*a, **kw):
330
            sys.stdout.writelines(difflib.unified_diff(*a, **kw))
331
            print
332
        
333
        if file_state in ['.', '?', 'I']:
334
            continue
335
        elif file_state == 'A':
336
            print '*** added %s %r' % (kind, new_name)
337
            if kind == 'file':
338
                diffit([],
339
                       new_tree.get_file(fid).readlines(),
340
                       fromfile=DEVNULL,
341
                       tofile=new_label + new_name + idlabel)
342
        elif file_state == 'D':
343
            assert isinstance(old_name, types.StringTypes)
344
            print '*** deleted %s %r' % (kind, old_name)
345
            if kind == 'file':
346
                diffit(old_tree.get_file(fid).readlines(), [],
347
                       fromfile=old_label + old_name + idlabel,
348
                       tofile=DEVNULL)
349
        elif file_state in ['M', 'R']:
350
            if file_state == 'M':
351
                assert kind == 'file'
352
                assert old_name == new_name
353
                print '*** modified %s %r' % (kind, new_name)
354
            elif file_state == 'R':
355
                print '*** renamed %s %r => %r' % (kind, old_name, new_name)
356
357
            if kind == 'file':
358
                diffit(old_tree.get_file(fid).readlines(),
359
                       new_tree.get_file(fid).readlines(),
360
                       fromfile=old_label + old_name + idlabel,
361
                       tofile=new_label + new_name)
362
        else:
363
            bailout("can't represent state %s {%s}" % (file_state, fid))
364
365
366
13 by mbp at sourcefrog
fix up cmd_log args
367
def cmd_log(timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
368
    """Show log of this branch.
369
370
    :todo: Options for utc; to show ids; to limit range; etc.
371
    """
12 by mbp at sourcefrog
new --timezone option for bzr log
372
    Branch('.').write_log(show_timezone=timezone)
1 by mbp at sourcefrog
import from baz patch-364
373
374
375
def cmd_ls(revision=None, verbose=False):
376
    """List files in a tree.
377
378
    :todo: Take a revision or remote path and list that tree instead.
379
    """
380
    b = Branch('.')
381
    if revision == None:
382
        tree = b.working_tree()
383
    else:
384
        tree = b.revision_tree(b.lookup_revision(revision))
385
        
386
    for fp, fc, kind, fid in tree.list_files():
387
        if verbose:
388
            if kind == 'directory':
389
                kindch = '/'
390
            elif kind == 'file':
391
                kindch = ''
392
            else:
393
                kindch = '???'
394
                
395
            print '%-8s %s%s' % (fc, fp, kindch)
396
        else:
397
            print fp
398
    
399
    
400
401
def cmd_unknowns():
402
    """List unknown files"""
403
    for f in Branch('.').unknowns():
404
        print quotefn(f)
405
406
407
def cmd_lookup_revision(revno):
408
    try:
409
        revno = int(revno)
410
    except ValueError:
411
        bailout("usage: lookup-revision REVNO",
412
                ["REVNO is a non-negative revision number for this branch"])
413
414
    print Branch('.').lookup_revision(revno) or NONE_STRING
415
416
417
418
def cmd_export(revno, dest):
419
    """Export past revision to destination directory."""
420
    b = Branch('.')
421
    rh = b.lookup_revision(int(revno))
422
    t = b.revision_tree(rh)
423
    t.export(dest)
424
425
426
427
######################################################################
428
# internal/test commands
429
430
431
def cmd_uuid():
432
    """Print a newly-generated UUID."""
433
    print uuid()
434
435
436
8 by mbp at sourcefrog
store committer's timezone in revision and show
437
def cmd_local_time_offset():
438
    print bzrlib.osutils.local_time_offset()
439
440
441
1 by mbp at sourcefrog
import from baz patch-364
442
def cmd_commit(message, verbose=False):
443
    Branch('.').commit(message, verbose=verbose)
444
445
446
def cmd_check():
447
    """Check consistency of the branch."""
448
    check()
449
450
451
def cmd_is(pred, *rest):
452
    """Test whether PREDICATE is true."""
453
    try:
454
        cmd_handler = globals()['assert_' + pred.replace('-', '_')]
455
    except KeyError:
456
        bailout("unknown predicate: %s" % quotefn(pred))
457
        
458
    try:
459
        cmd_handler(*rest)
460
    except BzrCheckError:
461
        # by default we don't print the message so that this can
462
        # be used from shell scripts without producing noise
463
        sys.exit(1)
464
465
466
def cmd_username():
467
    print bzrlib.osutils.username()
468
469
470
def cmd_user_email():
471
    print bzrlib.osutils.user_email()
472
473
474
def cmd_gen_revision_id():
475
    import time
476
    print bzrlib.branch._gen_revision_id(time.time())
477
478
479
def cmd_doctest():
480
    """Run internal doctest suite"""
481
    ## -v, if present, is seen by doctest; the argument is just here
482
    ## so our parser doesn't complain
483
484
    ## TODO: --verbose option
485
    
486
    import bzr, doctest, bzrlib.store
487
    bzrlib.trace.verbose = False
488
    doctest.testmod(bzr)
489
    doctest.testmod(bzrlib.store)
490
    doctest.testmod(bzrlib.inventory)
491
    doctest.testmod(bzrlib.branch)
492
    doctest.testmod(bzrlib.osutils)
493
    doctest.testmod(bzrlib.tree)
494
495
    # more strenuous tests;
496
    import bzrlib.tests
497
    doctest.testmod(bzrlib.tests)
498
499
500
######################################################################
501
# help
502
503
504
def cmd_help():
505
    # TODO: Specific help for particular commands
506
    print __doc__
507
508
509
def cmd_version():
510
    print "bzr (bazaar-ng) %s" % __version__
511
    print __copyright__
512
    print "http://bazaar-ng.org/"
513
    print
514
    print \
515
"""bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and
516
you may use, modify and redistribute it under the terms of the GNU 
517
General Public License version 2 or later."""
518
519
520
def cmd_rocks():
521
    """Statement of optimism."""
522
    print "it sure does!"
523
524
525
526
######################################################################
527
# main routine
528
529
530
# list of all available options; the rhs can be either None for an
531
# option that takes no argument, or a constructor function that checks
532
# the type.
533
OPTIONS = {
534
    'all':                    None,
535
    'help':                   None,
536
    'message':                unicode,
537
    'revision':               int,
538
    'show-ids':               None,
12 by mbp at sourcefrog
new --timezone option for bzr log
539
    'timezone':               str,
1 by mbp at sourcefrog
import from baz patch-364
540
    'verbose':                None,
541
    'version':                None,
542
    }
543
544
SHORT_OPTIONS = {
545
    'm':                      'message',
546
    'r':                      'revision',
547
    'v':                      'verbose',
548
}
549
550
# List of options that apply to particular commands; commands not
551
# listed take none.
552
cmd_options = {
553
    'add':                    ['verbose'],
554
    'commit':                 ['message', 'verbose'],
555
    'diff':                   ['revision'],
556
    'inventory':              ['revision'],
12 by mbp at sourcefrog
new --timezone option for bzr log
557
    'log':                    ['show-ids', 'timezone'],
1 by mbp at sourcefrog
import from baz patch-364
558
    'ls':                     ['revision', 'verbose'],
12 by mbp at sourcefrog
new --timezone option for bzr log
559
    'remove':                 ['verbose'],
1 by mbp at sourcefrog
import from baz patch-364
560
    'status':                 ['all'],
561
    }
562
563
564
cmd_args = {
565
    'init':                   [],
566
    'add':                    ['file+'],
567
    'commit':                 [],
568
    'diff':                   [],
569
    'file-id':                ['filename'],
570
    'get-file-text':          ['text_id'],
571
    'get-inventory':          ['inventory_id'],
572
    'get-revision':           ['revision_id'],
573
    'get-revision-inventory': ['revision_id'],
574
    'log':                    [],
575
    'lookup-revision':        ['revno'],
576
    'export':                 ['revno', 'dest'],
577
    'remove':                 ['file+'],
578
    'status':                 [],
579
    }
580
581
582
def parse_args(argv):
583
    """Parse command line.
584
    
585
    Arguments and options are parsed at this level before being passed
586
    down to specific command handlers.  This routine knows, from a
587
    lookup table, something about the available options, what optargs
588
    they take, and which commands will accept them.
589
590
    >>> parse_args('bzr --help'.split())
591
    ([], {'help': True})
592
    >>> parse_args('bzr --version'.split())
593
    ([], {'version': True})
594
    >>> parse_args('bzr status --all'.split())
595
    (['status'], {'all': True})
17 by mbp at sourcefrog
allow --option=ARG syntax
596
    >>> parse_args('bzr commit --message=biter'.split())
597
    (['commit'], {'message': u'biter'})
1 by mbp at sourcefrog
import from baz patch-364
598
    """
599
    args = []
600
    opts = {}
601
602
    # TODO: Maybe handle '--' to end options?
603
604
    it = iter(argv[1:])
605
    while it:
606
        a = it.next()
607
        if a[0] == '-':
17 by mbp at sourcefrog
allow --option=ARG syntax
608
            optarg = None
1 by mbp at sourcefrog
import from baz patch-364
609
            if a[1] == '-':
610
                mutter("  got option %r" % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
611
                if '=' in a:
612
                    optname, optarg = a[2:].split('=', 1)
613
                else:
614
                    optname = a[2:]
1 by mbp at sourcefrog
import from baz patch-364
615
                if optname not in OPTIONS:
616
                    bailout('unknown long option %r' % a)
617
            else:
618
                shortopt = a[1:]
619
                if shortopt not in SHORT_OPTIONS:
620
                    bailout('unknown short option %r' % a)
621
                optname = SHORT_OPTIONS[shortopt]
622
            
623
            if optname in opts:
624
                # XXX: Do we ever want to support this, e.g. for -r?
625
                bailout('repeated option %r' % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
626
                
1 by mbp at sourcefrog
import from baz patch-364
627
            optargfn = OPTIONS[optname]
628
            if optargfn:
17 by mbp at sourcefrog
allow --option=ARG syntax
629
                if optarg == None:
630
                    if not it:
631
                        bailout('option %r needs an argument' % a)
632
                    else:
633
                        optarg = it.next()
634
                opts[optname] = optargfn(optarg)
1 by mbp at sourcefrog
import from baz patch-364
635
                mutter("    option argument %r" % opts[optname])
636
            else:
17 by mbp at sourcefrog
allow --option=ARG syntax
637
                if optarg != None:
638
                    bailout('option %r takes no argument' % optname)
1 by mbp at sourcefrog
import from baz patch-364
639
                opts[optname] = True
640
        else:
641
            args.append(a)
642
643
    return args, opts
644
645
646
647
def _match_args(cmd, args):
648
    """Check non-option arguments match required pattern.
649
650
    >>> _match_args('status', ['asdasdsadasd'])
651
    Traceback (most recent call last):
652
    ...
653
    BzrError: ("extra arguments to command status: ['asdasdsadasd']", [])
654
    >>> _match_args('add', ['asdasdsadasd'])
655
    {'file_list': ['asdasdsadasd']}
656
    >>> _match_args('add', 'abc def gj'.split())
657
    {'file_list': ['abc', 'def', 'gj']}
658
    """
659
    # match argument pattern
660
    argform = cmd_args.get(cmd, [])
661
    argdict = {}
662
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
663
    # all but one.
664
    for ap in argform:
665
        argname = ap[:-1]
666
        if ap[-1] == '?':
667
            assert 0
668
        elif ap[-1] == '*':
669
            assert 0
670
        elif ap[-1] == '+':
671
            if not args:
672
                bailout("command %r needs one or more %s"
673
                        % (cmd, argname.upper()))
674
            else:
675
                argdict[argname + '_list'] = args[:]
676
                args = []
677
        else:
678
            # just a plain arg
679
            argname = ap
680
            if not args:
681
                bailout("command %r requires argument %s"
682
                        % (cmd, argname.upper()))
683
            else:
684
                argdict[argname] = args.pop(0)
685
            
686
    if args:
687
        bailout("extra arguments to command %s: %r"
688
                % (cmd, args))
689
690
    return argdict
691
692
693
694
def run_bzr(argv):
695
    """Execute a command.
696
697
    This is similar to main(), but without all the trappings for
698
    logging and error handling.
699
    """
700
    try:
701
        args, opts = parse_args(argv)
702
        if 'help' in opts:
703
            # TODO: pass down other arguments in case they asked for
704
            # help on a command name?
705
            cmd_help()
706
            return 0
707
        elif 'version' in opts:
708
            cmd_version()
709
            return 0
710
        cmd = args.pop(0)
711
    except IndexError:
712
        log_error('usage: bzr COMMAND\n')
713
        log_error('  try "bzr help"\n')
714
        return 1
715
            
716
    try:
717
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
718
    except KeyError:
719
        bailout("unknown command " + `cmd`)
720
721
    # TODO: special --profile option to turn on the Python profiler
722
723
    # check options are reasonable
724
    allowed = cmd_options.get(cmd, [])
725
    for oname in opts:
726
        if oname not in allowed:
727
            bailout("option %r is not allowed for command %r"
728
                    % (oname, cmd))
729
730
    cmdargs = _match_args(cmd, args)
731
    cmdargs.update(opts)
732
733
    ret = cmd_handler(**cmdargs) or 0
734
735
736
737
def main(argv):
738
    ## TODO: Handle command-line options; probably know what options are valid for
739
    ## each command
740
741
    ## TODO: If the arguments are wrong, give a usage message rather
742
    ## than just a backtrace.
743
744
    try:
745
        t = bzrlib.trace._tracefile
746
        t.write('-' * 60 + '\n')
747
        t.write('bzr invoked at %s\n' % format_date(time.time()))
748
        t.write('  by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
749
        t.write('  arguments: %r\n' % argv)
750
751
        starttime = os.times()[4]
752
753
        import platform
754
        t.write('  platform: %s\n' % platform.platform())
755
        t.write('  python: %s\n' % platform.python_version())
756
757
        ret = run_bzr(argv)
758
        
759
        times = os.times()
760
        mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
761
               % times[:4])
762
        mutter("    %.3f elapsed" % (times[4] - starttime))
763
764
        return ret
765
    except BzrError, e:
766
        log_error('bzr: error: ' + e.args[0] + '\n')
767
        if len(e.args) > 1:
768
            for h in e.args[1]:
769
                log_error('  ' + h + '\n')
770
        return 1
771
    except Exception, e:
772
        log_error('bzr: exception: %s\n' % e)
773
        log_error('    see .bzr.log for details\n')
774
        traceback.print_exc(None, bzrlib.trace._tracefile)
775
        traceback.print_exc(None, sys.stderr)
776
        return 1
777
778
    # TODO: Maybe nicer handling of IOError?
779
780
781
782
if __name__ == '__main__':
783
    sys.exit(main(sys.argv))
784
    ##import profile
785
    ##profile.run('main(sys.argv)')
786