/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')
197
    print 'revision number:', b.revno()
18 by mbp at sourcefrog
show count of versioned/unknown/ignored files
198
199
    count_version_dirs = 0
19 by mbp at sourcefrog
more information in info command
200
201
    count_status = {'A': 0, 'D': 0, 'M': 0, 'R': 0, '?': 0, 'I': 0, '.': 0}
202
    for st_tup in bzrlib.diff_trees(b.basis_tree(), b.working_tree()):
203
        fs = st_tup[0]
204
        count_status[fs] += 1
205
        if fs not in ['I', '?'] and st_tup[4] == 'directory':
206
            count_version_dirs += 1
207
208
    print
209
    print 'in the working tree:'
210
    for name, fs in (('unchanged', '.'),
211
                     ('modified', 'M'), ('added', 'A'), ('removed', 'D'),
212
                     ('renamed', 'R'), ('unknown', '?'), ('ignored', 'I'),
213
                     ):
214
        print '  %5d %s' % (count_status[fs], name)
215
    print '  %5d versioned subdirectories' % count_version_dirs
18 by mbp at sourcefrog
show count of versioned/unknown/ignored files
216
            
1 by mbp at sourcefrog
import from baz patch-364
217
218
219
def cmd_remove(file_list, verbose=False):
220
    Branch('.').remove(file_list, verbose=verbose)
221
222
223
224
def cmd_file_id(filename):
225
    i = Branch('.').read_working_inventory().path2id(filename)
226
    if i is None:
227
        bailout("%s is not a versioned file" % filename)
228
    else:
229
        print i
230
231
232
def cmd_find_filename(fileid):
233
    n = find_filename(fileid)
234
    if n is None:
235
        bailout("%s is not a live file id" % fileid)
236
    else:
237
        print n
238
239
240
def cmd_revision_history():
241
    for patchid in Branch('.').revision_history():
242
        print patchid
243
244
245
246
def cmd_init():
247
    # TODO: Check we're not already in a working directory?  At the
248
    # moment you'll get an ugly error.
249
    
250
    # TODO: What if we're in a subdirectory of a branch?  Would like
251
    # to allow that, but then the parent may need to understand that
252
    # the children have disappeared, or should they be versioned in
253
    # both?
254
255
    # TODO: Take an argument/option for branch name.
256
    Branch('.', init=True)
257
258
259
def cmd_diff(revision=None):
260
    """Show diff from basis to working copy.
261
262
    :todo: Take one or two revision arguments, look up those trees,
263
           and diff them.
264
265
    :todo: Allow diff across branches.
266
267
    :todo: Mangle filenames in diff to be more relevant.
268
269
    :todo: Shouldn't be in the cmd function.
270
    """
271
272
    b = Branch('.')
273
274
    if revision == None:
275
        old_tree = b.basis_tree()
276
    else:
277
        old_tree = b.revision_tree(b.lookup_revision(revision))
278
        
279
    new_tree = b.working_tree()
280
    old_inv = old_tree.inventory
281
    new_inv = new_tree.inventory
282
283
    # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
284
    old_label = ''
285
    new_label = ''
286
287
    DEVNULL = '/dev/null'
288
    # Windows users, don't panic about this filename -- it is a
289
    # special signal to GNU patch that the file should be created or
290
    # deleted respectively.
291
292
    # TODO: Generation of pseudo-diffs for added/deleted files could
293
    # be usefully made into a much faster special case.
294
295
    # TODO: Better to return them in sorted order I think.
296
    
297
    for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
298
        d = None
299
300
        # Don't show this by default; maybe do it if an option is passed
301
        # idlabel = '      {%s}' % fid
302
        idlabel = ''
303
304
        # FIXME: Something about the diff format makes patch unhappy
305
        # with newly-added files.
306
307
        def diffit(*a, **kw):
308
            sys.stdout.writelines(difflib.unified_diff(*a, **kw))
309
            print
310
        
311
        if file_state in ['.', '?', 'I']:
312
            continue
313
        elif file_state == 'A':
314
            print '*** added %s %r' % (kind, new_name)
315
            if kind == 'file':
316
                diffit([],
317
                       new_tree.get_file(fid).readlines(),
318
                       fromfile=DEVNULL,
319
                       tofile=new_label + new_name + idlabel)
320
        elif file_state == 'D':
321
            assert isinstance(old_name, types.StringTypes)
322
            print '*** deleted %s %r' % (kind, old_name)
323
            if kind == 'file':
324
                diffit(old_tree.get_file(fid).readlines(), [],
325
                       fromfile=old_label + old_name + idlabel,
326
                       tofile=DEVNULL)
327
        elif file_state in ['M', 'R']:
328
            if file_state == 'M':
329
                assert kind == 'file'
330
                assert old_name == new_name
331
                print '*** modified %s %r' % (kind, new_name)
332
            elif file_state == 'R':
333
                print '*** renamed %s %r => %r' % (kind, old_name, new_name)
334
335
            if kind == 'file':
336
                diffit(old_tree.get_file(fid).readlines(),
337
                       new_tree.get_file(fid).readlines(),
338
                       fromfile=old_label + old_name + idlabel,
339
                       tofile=new_label + new_name)
340
        else:
341
            bailout("can't represent state %s {%s}" % (file_state, fid))
342
343
344
13 by mbp at sourcefrog
fix up cmd_log args
345
def cmd_log(timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
346
    """Show log of this branch.
347
348
    :todo: Options for utc; to show ids; to limit range; etc.
349
    """
12 by mbp at sourcefrog
new --timezone option for bzr log
350
    Branch('.').write_log(show_timezone=timezone)
1 by mbp at sourcefrog
import from baz patch-364
351
352
353
def cmd_ls(revision=None, verbose=False):
354
    """List files in a tree.
355
356
    :todo: Take a revision or remote path and list that tree instead.
357
    """
358
    b = Branch('.')
359
    if revision == None:
360
        tree = b.working_tree()
361
    else:
362
        tree = b.revision_tree(b.lookup_revision(revision))
363
        
364
    for fp, fc, kind, fid in tree.list_files():
365
        if verbose:
366
            if kind == 'directory':
367
                kindch = '/'
368
            elif kind == 'file':
369
                kindch = ''
370
            else:
371
                kindch = '???'
372
                
373
            print '%-8s %s%s' % (fc, fp, kindch)
374
        else:
375
            print fp
376
    
377
    
378
379
def cmd_unknowns():
380
    """List unknown files"""
381
    for f in Branch('.').unknowns():
382
        print quotefn(f)
383
384
385
def cmd_lookup_revision(revno):
386
    try:
387
        revno = int(revno)
388
    except ValueError:
389
        bailout("usage: lookup-revision REVNO",
390
                ["REVNO is a non-negative revision number for this branch"])
391
392
    print Branch('.').lookup_revision(revno) or NONE_STRING
393
394
395
396
def cmd_export(revno, dest):
397
    """Export past revision to destination directory."""
398
    b = Branch('.')
399
    rh = b.lookup_revision(int(revno))
400
    t = b.revision_tree(rh)
401
    t.export(dest)
402
403
404
405
######################################################################
406
# internal/test commands
407
408
409
def cmd_uuid():
410
    """Print a newly-generated UUID."""
411
    print uuid()
412
413
414
8 by mbp at sourcefrog
store committer's timezone in revision and show
415
def cmd_local_time_offset():
416
    print bzrlib.osutils.local_time_offset()
417
418
419
1 by mbp at sourcefrog
import from baz patch-364
420
def cmd_commit(message, verbose=False):
421
    Branch('.').commit(message, verbose=verbose)
422
423
424
def cmd_check():
425
    """Check consistency of the branch."""
426
    check()
427
428
429
def cmd_is(pred, *rest):
430
    """Test whether PREDICATE is true."""
431
    try:
432
        cmd_handler = globals()['assert_' + pred.replace('-', '_')]
433
    except KeyError:
434
        bailout("unknown predicate: %s" % quotefn(pred))
435
        
436
    try:
437
        cmd_handler(*rest)
438
    except BzrCheckError:
439
        # by default we don't print the message so that this can
440
        # be used from shell scripts without producing noise
441
        sys.exit(1)
442
443
444
def cmd_username():
445
    print bzrlib.osutils.username()
446
447
448
def cmd_user_email():
449
    print bzrlib.osutils.user_email()
450
451
452
def cmd_gen_revision_id():
453
    import time
454
    print bzrlib.branch._gen_revision_id(time.time())
455
456
457
def cmd_doctest():
458
    """Run internal doctest suite"""
459
    ## -v, if present, is seen by doctest; the argument is just here
460
    ## so our parser doesn't complain
461
462
    ## TODO: --verbose option
463
    
464
    import bzr, doctest, bzrlib.store
465
    bzrlib.trace.verbose = False
466
    doctest.testmod(bzr)
467
    doctest.testmod(bzrlib.store)
468
    doctest.testmod(bzrlib.inventory)
469
    doctest.testmod(bzrlib.branch)
470
    doctest.testmod(bzrlib.osutils)
471
    doctest.testmod(bzrlib.tree)
472
473
    # more strenuous tests;
474
    import bzrlib.tests
475
    doctest.testmod(bzrlib.tests)
476
477
478
######################################################################
479
# help
480
481
482
def cmd_help():
483
    # TODO: Specific help for particular commands
484
    print __doc__
485
486
487
def cmd_version():
488
    print "bzr (bazaar-ng) %s" % __version__
489
    print __copyright__
490
    print "http://bazaar-ng.org/"
491
    print
492
    print \
493
"""bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and
494
you may use, modify and redistribute it under the terms of the GNU 
495
General Public License version 2 or later."""
496
497
498
def cmd_rocks():
499
    """Statement of optimism."""
500
    print "it sure does!"
501
502
503
504
######################################################################
505
# main routine
506
507
508
# list of all available options; the rhs can be either None for an
509
# option that takes no argument, or a constructor function that checks
510
# the type.
511
OPTIONS = {
512
    'all':                    None,
513
    'help':                   None,
514
    'message':                unicode,
515
    'revision':               int,
516
    'show-ids':               None,
12 by mbp at sourcefrog
new --timezone option for bzr log
517
    'timezone':               str,
1 by mbp at sourcefrog
import from baz patch-364
518
    'verbose':                None,
519
    'version':                None,
520
    }
521
522
SHORT_OPTIONS = {
523
    'm':                      'message',
524
    'r':                      'revision',
525
    'v':                      'verbose',
526
}
527
528
# List of options that apply to particular commands; commands not
529
# listed take none.
530
cmd_options = {
531
    'add':                    ['verbose'],
532
    'commit':                 ['message', 'verbose'],
533
    'diff':                   ['revision'],
534
    'inventory':              ['revision'],
12 by mbp at sourcefrog
new --timezone option for bzr log
535
    'log':                    ['show-ids', 'timezone'],
1 by mbp at sourcefrog
import from baz patch-364
536
    'ls':                     ['revision', 'verbose'],
12 by mbp at sourcefrog
new --timezone option for bzr log
537
    'remove':                 ['verbose'],
1 by mbp at sourcefrog
import from baz patch-364
538
    'status':                 ['all'],
539
    }
540
541
542
cmd_args = {
543
    'init':                   [],
544
    'add':                    ['file+'],
545
    'commit':                 [],
546
    'diff':                   [],
547
    'file-id':                ['filename'],
548
    'get-file-text':          ['text_id'],
549
    'get-inventory':          ['inventory_id'],
550
    'get-revision':           ['revision_id'],
551
    'get-revision-inventory': ['revision_id'],
552
    'log':                    [],
553
    'lookup-revision':        ['revno'],
554
    'export':                 ['revno', 'dest'],
555
    'remove':                 ['file+'],
556
    'status':                 [],
557
    }
558
559
560
def parse_args(argv):
561
    """Parse command line.
562
    
563
    Arguments and options are parsed at this level before being passed
564
    down to specific command handlers.  This routine knows, from a
565
    lookup table, something about the available options, what optargs
566
    they take, and which commands will accept them.
567
568
    >>> parse_args('bzr --help'.split())
569
    ([], {'help': True})
570
    >>> parse_args('bzr --version'.split())
571
    ([], {'version': True})
572
    >>> parse_args('bzr status --all'.split())
573
    (['status'], {'all': True})
17 by mbp at sourcefrog
allow --option=ARG syntax
574
    >>> parse_args('bzr commit --message=biter'.split())
575
    (['commit'], {'message': u'biter'})
1 by mbp at sourcefrog
import from baz patch-364
576
    """
577
    args = []
578
    opts = {}
579
580
    # TODO: Maybe handle '--' to end options?
581
582
    it = iter(argv[1:])
583
    while it:
584
        a = it.next()
585
        if a[0] == '-':
17 by mbp at sourcefrog
allow --option=ARG syntax
586
            optarg = None
1 by mbp at sourcefrog
import from baz patch-364
587
            if a[1] == '-':
588
                mutter("  got option %r" % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
589
                if '=' in a:
590
                    optname, optarg = a[2:].split('=', 1)
591
                else:
592
                    optname = a[2:]
1 by mbp at sourcefrog
import from baz patch-364
593
                if optname not in OPTIONS:
594
                    bailout('unknown long option %r' % a)
595
            else:
596
                shortopt = a[1:]
597
                if shortopt not in SHORT_OPTIONS:
598
                    bailout('unknown short option %r' % a)
599
                optname = SHORT_OPTIONS[shortopt]
600
            
601
            if optname in opts:
602
                # XXX: Do we ever want to support this, e.g. for -r?
603
                bailout('repeated option %r' % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
604
                
1 by mbp at sourcefrog
import from baz patch-364
605
            optargfn = OPTIONS[optname]
606
            if optargfn:
17 by mbp at sourcefrog
allow --option=ARG syntax
607
                if optarg == None:
608
                    if not it:
609
                        bailout('option %r needs an argument' % a)
610
                    else:
611
                        optarg = it.next()
612
                opts[optname] = optargfn(optarg)
1 by mbp at sourcefrog
import from baz patch-364
613
                mutter("    option argument %r" % opts[optname])
614
            else:
17 by mbp at sourcefrog
allow --option=ARG syntax
615
                if optarg != None:
616
                    bailout('option %r takes no argument' % optname)
1 by mbp at sourcefrog
import from baz patch-364
617
                opts[optname] = True
618
        else:
619
            args.append(a)
620
621
    return args, opts
622
623
624
625
def _match_args(cmd, args):
626
    """Check non-option arguments match required pattern.
627
628
    >>> _match_args('status', ['asdasdsadasd'])
629
    Traceback (most recent call last):
630
    ...
631
    BzrError: ("extra arguments to command status: ['asdasdsadasd']", [])
632
    >>> _match_args('add', ['asdasdsadasd'])
633
    {'file_list': ['asdasdsadasd']}
634
    >>> _match_args('add', 'abc def gj'.split())
635
    {'file_list': ['abc', 'def', 'gj']}
636
    """
637
    # match argument pattern
638
    argform = cmd_args.get(cmd, [])
639
    argdict = {}
640
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
641
    # all but one.
642
    for ap in argform:
643
        argname = ap[:-1]
644
        if ap[-1] == '?':
645
            assert 0
646
        elif ap[-1] == '*':
647
            assert 0
648
        elif ap[-1] == '+':
649
            if not args:
650
                bailout("command %r needs one or more %s"
651
                        % (cmd, argname.upper()))
652
            else:
653
                argdict[argname + '_list'] = args[:]
654
                args = []
655
        else:
656
            # just a plain arg
657
            argname = ap
658
            if not args:
659
                bailout("command %r requires argument %s"
660
                        % (cmd, argname.upper()))
661
            else:
662
                argdict[argname] = args.pop(0)
663
            
664
    if args:
665
        bailout("extra arguments to command %s: %r"
666
                % (cmd, args))
667
668
    return argdict
669
670
671
672
def run_bzr(argv):
673
    """Execute a command.
674
675
    This is similar to main(), but without all the trappings for
676
    logging and error handling.
677
    """
678
    try:
679
        args, opts = parse_args(argv)
680
        if 'help' in opts:
681
            # TODO: pass down other arguments in case they asked for
682
            # help on a command name?
683
            cmd_help()
684
            return 0
685
        elif 'version' in opts:
686
            cmd_version()
687
            return 0
688
        cmd = args.pop(0)
689
    except IndexError:
690
        log_error('usage: bzr COMMAND\n')
691
        log_error('  try "bzr help"\n')
692
        return 1
693
            
694
    try:
695
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
696
    except KeyError:
697
        bailout("unknown command " + `cmd`)
698
699
    # TODO: special --profile option to turn on the Python profiler
700
701
    # check options are reasonable
702
    allowed = cmd_options.get(cmd, [])
703
    for oname in opts:
704
        if oname not in allowed:
705
            bailout("option %r is not allowed for command %r"
706
                    % (oname, cmd))
707
708
    cmdargs = _match_args(cmd, args)
709
    cmdargs.update(opts)
710
711
    ret = cmd_handler(**cmdargs) or 0
712
713
714
715
def main(argv):
716
    ## TODO: Handle command-line options; probably know what options are valid for
717
    ## each command
718
719
    ## TODO: If the arguments are wrong, give a usage message rather
720
    ## than just a backtrace.
721
722
    try:
723
        t = bzrlib.trace._tracefile
724
        t.write('-' * 60 + '\n')
725
        t.write('bzr invoked at %s\n' % format_date(time.time()))
726
        t.write('  by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
727
        t.write('  arguments: %r\n' % argv)
728
729
        starttime = os.times()[4]
730
731
        import platform
732
        t.write('  platform: %s\n' % platform.platform())
733
        t.write('  python: %s\n' % platform.python_version())
734
735
        ret = run_bzr(argv)
736
        
737
        times = os.times()
738
        mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
739
               % times[:4])
740
        mutter("    %.3f elapsed" % (times[4] - starttime))
741
742
        return ret
743
    except BzrError, e:
744
        log_error('bzr: error: ' + e.args[0] + '\n')
745
        if len(e.args) > 1:
746
            for h in e.args[1]:
747
                log_error('  ' + h + '\n')
748
        return 1
749
    except Exception, e:
750
        log_error('bzr: exception: %s\n' % e)
751
        log_error('    see .bzr.log for details\n')
752
        traceback.print_exc(None, bzrlib.trace._tracefile)
753
        traceback.print_exc(None, sys.stderr)
754
        return 1
755
756
    # TODO: Maybe nicer handling of IOError?
757
758
759
760
if __name__ == '__main__':
761
    sys.exit(main(sys.argv))
762
    ##import profile
763
    ##profile.run('main(sys.argv)')
764