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