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