/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
# Copyright (C) 2004, 2005 by Martin Pool
2
# Copyright (C) 2005 by Canonical Ltd
3
4
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19
"""Bazaar-NG -- a free distributed version-control tool
20
21
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
22
23
Current limitation include:
24
25
* Metadata format is not stable yet -- you may need to
26
  discard history in the future.
27
28
* Insufficient error handling.
29
30
* Many commands unimplemented or partially implemented.
31
32
* Space-inefficient storage.
33
34
* No merge operators yet.
35
36
Interesting commands::
37
85 by mbp at sourcefrog
improved help string
38
  bzr help [COMMAND]
39
       Show help screen
1 by mbp at sourcefrog
import from baz patch-364
40
  bzr version
41
       Show software version/licence/non-warranty.
42
  bzr init
43
       Start versioning the current directory
44
  bzr add FILE...
45
       Make files versioned.
46
  bzr log
47
       Show revision history.
48
  bzr diff
49
       Show changes from last revision to working copy.
50
  bzr commit -m 'MESSAGE'
51
       Store current state as new revision.
52
  bzr export REVNO DESTINATION
53
       Export the branch state at a previous version.
54
  bzr status
55
       Show summary of pending changes.
56
  bzr remove FILE...
57
       Make a file not versioned.
76 by mbp at sourcefrog
mention "info" in top-level help
58
  bzr info
59
       Show statistics about this branch.
1 by mbp at sourcefrog
import from baz patch-364
60
"""
61
62
63
64
65
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
66
import traceback, socket, fnmatch, difflib
67
from os import path
68
from sets import Set
69
from pprint import pprint
70
from stat import *
71
from glob import glob
72
73
import bzrlib
74
from bzrlib.store import ImmutableStore
75
from bzrlib.trace import mutter, note, log_error
76
from bzrlib.errors import bailout, BzrError
77
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
78
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
79
from bzrlib.revision import Revision
80
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
81
     format_date
82
83
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
84
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
85
86
## standard representation
87
NONE_STRING = '(none)'
88
EMPTY = 'empty'
89
90
91
## TODO: Perhaps a different version of inventory commands that
92
## returns iterators...
93
94
## TODO: Perhaps an AtomicFile class that writes to a temporary file and then renames.
95
96
## TODO: Some kind of locking on branches.  Perhaps there should be a
97
## parameter to the branch object saying whether we want a read or
98
## write lock; release it from destructor.  Perhaps don't even need a
99
## read lock to look at immutable objects?
100
101
## TODO: Perhaps make UUIDs predictable in test mode to make it easier
102
## to compare output?
103
34 by mbp at sourcefrog
doc
104
## TODO: Some kind of global code to generate the right Branch object
105
## to work on.  Almost, but not quite all, commands need one, and it
106
## can be taken either from their parameters or their working
107
## directory.
108
46 by Martin Pool
todo
109
## TODO: rename command, needed soon: check destination doesn't exist
110
## either in working copy or tree; move working copy; update
111
## inventory; write out
112
113
## TODO: move command; check destination is a directory and will not
114
## clash; move it.
115
116
## TODO: command to show renames, one per line, as to->from
117
118
1 by mbp at sourcefrog
import from baz patch-364
119
120
121
def cmd_status(all=False):
122
    """Display status summary.
123
124
    For each file there is a single line giving its file state and name.
125
    The name is that in the current revision unless it is deleted or
126
    missing, in which case the old name is shown.
127
128
    :todo: Don't show unchanged files unless ``--all`` is given?
129
    """
130
    Branch('.').show_status(show_all=all)
131
132
133
134
######################################################################
135
# examining history
136
def cmd_get_revision(revision_id):
137
    Branch('.').get_revision(revision_id).write_xml(sys.stdout)
138
139
140
def cmd_get_file_text(text_id):
141
    """Get contents of a file by hash."""
142
    sf = Branch('.').text_store[text_id]
143
    pumpfile(sf, sys.stdout)
144
145
146
147
######################################################################
148
# commands
149
    
150
151
def cmd_revno():
152
    """Show number of revisions on this branch"""
153
    print Branch('.').revno()
154
    
155
70 by mbp at sourcefrog
Prepare for smart recursive add.
156
    
1 by mbp at sourcefrog
import from baz patch-364
157
def cmd_add(file_list, verbose=False):
70 by mbp at sourcefrog
Prepare for smart recursive add.
158
    """Add specified files or directories.
159
160
    In non-recursive mode, all the named items are added, regardless
161
    of whether they were previously ignored.  A warning is given if
162
    any of the named files are already versioned.
163
164
    In recursive mode (the default), files are treated the same way
165
    but the behaviour for directories is different.  Directories that
166
    are already versioned do not give a warning.  All directories,
167
    whether already versioned or not, are searched for files or
168
    subdirectories that are neither versioned or ignored, and these
169
    are added.  This search proceeds recursively into versioned
170
    directories.
171
172
    Therefore simply saying 'bzr add .' will version all files that
173
    are currently unknown.
174
    """
87 by mbp at sourcefrog
- clean up smart_add code, and make it commit the inventory
175
    bzrlib.add.smart_add(file_list, 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):
87 by mbp at sourcefrog
- clean up smart_add code, and make it commit the inventory
179
    """Show path of file relative to root"""
68 by mbp at sourcefrog
- new relpath command and function
180
    print Branch(filename).relpath(filename)
181
182
1 by mbp at sourcefrog
import from baz patch-364
183
def cmd_inventory(revision=None):
184
    """Show inventory of the current working copy."""
185
    ## TODO: Also optionally show a previous inventory
186
    ## TODO: Format options
187
    b = Branch('.')
188
    if revision == None:
189
        inv = b.read_working_inventory()
190
    else:
191
        inv = b.get_revision_inventory(b.lookup_revision(revision))
192
        
193
    for path, entry in inv.iter_entries():
194
        print '%-50s %s' % (entry.file_id, path)
195
196
197
198
def cmd_info():
112 by mbp at sourcefrog
help for info command
199
    """info: Show statistical information for this branch
200
201
usage: bzr info"""
77 by mbp at sourcefrog
- split info command out into separate file
202
    import info
203
    info.show_info(Branch('.'))        
21 by mbp at sourcefrog
- bzr info: show summary information on branch history
204
    
1 by mbp at sourcefrog
import from baz patch-364
205
206
207
def cmd_remove(file_list, verbose=False):
69 by Martin Pool
handle add, remove, file-id being given filenames that are
208
    b = Branch(file_list[0])
209
    b.remove([b.relpath(f) for f in file_list], verbose=verbose)
1 by mbp at sourcefrog
import from baz patch-364
210
211
212
213
def cmd_file_id(filename):
69 by Martin Pool
handle add, remove, file-id being given filenames that are
214
    b = Branch(filename)
215
    i = b.inventory.path2id(b.relpath(filename))
1 by mbp at sourcefrog
import from baz patch-364
216
    if i is None:
217
        bailout("%s is not a versioned file" % filename)
218
    else:
219
        print i
220
221
222
def cmd_find_filename(fileid):
223
    n = find_filename(fileid)
224
    if n is None:
225
        bailout("%s is not a live file id" % fileid)
226
    else:
227
        print n
228
229
230
def cmd_revision_history():
231
    for patchid in Branch('.').revision_history():
232
        print patchid
233
234
156 by mbp at sourcefrog
new "directories" command
235
def cmd_directories():
236
    for name, ie in Branch('.').read_working_inventory().directories():
237
        if name == '':
238
            print '.'
239
        else:
240
            print name
241
1 by mbp at sourcefrog
import from baz patch-364
242
243
def cmd_init():
244
    # TODO: Check we're not already in a working directory?  At the
245
    # moment you'll get an ugly error.
246
    
247
    # TODO: What if we're in a subdirectory of a branch?  Would like
248
    # to allow that, but then the parent may need to understand that
249
    # the children have disappeared, or should they be versioned in
250
    # both?
251
252
    # TODO: Take an argument/option for branch name.
253
    Branch('.', init=True)
254
255
256
def cmd_diff(revision=None):
109 by mbp at sourcefrog
more help for diff command
257
    """bzr diff: Show differences in working tree.
258
    
259
usage: bzr diff [-r REV]
260
261
--revision REV
262
    Show changes since REV, rather than predecessor.
263
264
TODO: Given two revision arguments, show the difference between them.
265
266
TODO: Allow diff across branches.
267
268
TODO: Option to use external diff command; could be GNU diff, wdiff,
269
or a graphical diff.
270
271
TODO: Diff selected files.
272
"""
273
274
    ## TODO: Shouldn't be in the cmd function.
1 by mbp at sourcefrog
import from baz patch-364
275
276
    b = Branch('.')
277
278
    if revision == None:
279
        old_tree = b.basis_tree()
280
    else:
281
        old_tree = b.revision_tree(b.lookup_revision(revision))
282
        
283
    new_tree = b.working_tree()
284
    old_inv = old_tree.inventory
285
    new_inv = new_tree.inventory
286
287
    # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
288
    old_label = ''
289
    new_label = ''
290
291
    DEVNULL = '/dev/null'
292
    # Windows users, don't panic about this filename -- it is a
293
    # special signal to GNU patch that the file should be created or
294
    # deleted respectively.
295
296
    # TODO: Generation of pseudo-diffs for added/deleted files could
297
    # be usefully made into a much faster special case.
298
299
    # TODO: Better to return them in sorted order I think.
300
    
301
    for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
302
        d = None
303
304
        # Don't show this by default; maybe do it if an option is passed
305
        # idlabel = '      {%s}' % fid
306
        idlabel = ''
307
308
        # FIXME: Something about the diff format makes patch unhappy
309
        # with newly-added files.
310
311
        def diffit(*a, **kw):
312
            sys.stdout.writelines(difflib.unified_diff(*a, **kw))
313
            print
314
        
315
        if file_state in ['.', '?', 'I']:
316
            continue
317
        elif file_state == 'A':
318
            print '*** added %s %r' % (kind, new_name)
319
            if kind == 'file':
320
                diffit([],
321
                       new_tree.get_file(fid).readlines(),
322
                       fromfile=DEVNULL,
323
                       tofile=new_label + new_name + idlabel)
324
        elif file_state == 'D':
325
            assert isinstance(old_name, types.StringTypes)
326
            print '*** deleted %s %r' % (kind, old_name)
327
            if kind == 'file':
328
                diffit(old_tree.get_file(fid).readlines(), [],
329
                       fromfile=old_label + old_name + idlabel,
330
                       tofile=DEVNULL)
331
        elif file_state in ['M', 'R']:
332
            if file_state == 'M':
333
                assert kind == 'file'
334
                assert old_name == new_name
335
                print '*** modified %s %r' % (kind, new_name)
336
            elif file_state == 'R':
337
                print '*** renamed %s %r => %r' % (kind, old_name, new_name)
338
339
            if kind == 'file':
340
                diffit(old_tree.get_file(fid).readlines(),
341
                       new_tree.get_file(fid).readlines(),
342
                       fromfile=old_label + old_name + idlabel,
343
                       tofile=new_label + new_name)
344
        else:
345
            bailout("can't represent state %s {%s}" % (file_state, fid))
346
347
348
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
349
def cmd_deleted(show_ids=False):
135 by mbp at sourcefrog
Simple new 'deleted' command
350
    """List files deleted in the working tree.
351
352
TODO: Show files deleted since a previous revision, or between two revisions.
353
    """
354
    b = Branch('.')
355
    old = b.basis_tree()
356
    new = b.working_tree()
357
147 by mbp at sourcefrog
todo
358
    ## TODO: Much more efficient way to do this: read in new
359
    ## directories with readdir, rather than stating each one.  Same
360
    ## level of effort but possibly much less IO.  (Or possibly not,
361
    ## if the directories are very large...)
362
135 by mbp at sourcefrog
Simple new 'deleted' command
363
    for path, ie in old.inventory.iter_entries():
364
        if not new.has_id(ie.file_id):
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
365
            if show_ids:
366
                print '%-50s %s' % (path, ie.file_id)
367
            else:
368
                print path
148 by mbp at sourcefrog
performance notes and measurements
369
370
371
372
def cmd_parse_inventory():
373
    import cElementTree
374
    
375
    cElementTree.ElementTree().parse(file('.bzr/inventory'))
376
377
378
379
def cmd_load_inventory():
149 by mbp at sourcefrog
experiment with new nested inventory file format
380
    inv = Branch('.').basis_tree().inventory
381
382
383
384
def cmd_dump_new_inventory():
385
    import bzrlib.newinventory
386
    inv = Branch('.').basis_tree().inventory
387
    bzrlib.newinventory.write_inventory(inv, sys.stdout)
151 by mbp at sourcefrog
experimental nested-inventory load support
388
389
390
def cmd_load_new_inventory():
391
    import bzrlib.newinventory
392
    bzrlib.newinventory.read_new_inventory(sys.stdin)
149 by mbp at sourcefrog
experiment with new nested inventory file format
393
                
394
    
395
def cmd_dump_slacker_inventory():
396
    import bzrlib.newinventory
397
    inv = Branch('.').basis_tree().inventory
398
    bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
399
                
400
    
135 by mbp at sourcefrog
Simple new 'deleted' command
401
65 by mbp at sourcefrog
rename 'find-branch-root' command to just 'root'
402
def cmd_root(filename=None):
403
    """Print the branch root."""
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
404
    print bzrlib.branch.find_branch_root(filename)
405
    
406
13 by mbp at sourcefrog
fix up cmd_log args
407
def cmd_log(timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
408
    """Show log of this branch.
409
410
    :todo: Options for utc; to show ids; to limit range; etc.
411
    """
12 by mbp at sourcefrog
new --timezone option for bzr log
412
    Branch('.').write_log(show_timezone=timezone)
1 by mbp at sourcefrog
import from baz patch-364
413
414
415
def cmd_ls(revision=None, verbose=False):
416
    """List files in a tree.
417
418
    :todo: Take a revision or remote path and list that tree instead.
419
    """
420
    b = Branch('.')
421
    if revision == None:
422
        tree = b.working_tree()
423
    else:
424
        tree = b.revision_tree(b.lookup_revision(revision))
425
        
426
    for fp, fc, kind, fid in tree.list_files():
427
        if verbose:
428
            if kind == 'directory':
429
                kindch = '/'
430
            elif kind == 'file':
431
                kindch = ''
432
            else:
433
                kindch = '???'
434
                
435
            print '%-8s %s%s' % (fc, fp, kindch)
436
        else:
437
            print fp
438
    
439
    
440
441
def cmd_unknowns():
442
    """List unknown files"""
443
    for f in Branch('.').unknowns():
444
        print quotefn(f)
445
446
133 by mbp at sourcefrog
- new 'ignored' command
447
448
def cmd_ignored(verbose=True):
449
    """List ignored files and the patterns that matched them.
450
      """
451
    tree = Branch('.').working_tree()
452
    for path, file_class, kind, id in tree.list_files():
453
        if file_class != 'I':
454
            continue
455
        ## XXX: Slightly inefficient since this was already calculated
456
        pat = tree.is_ignored(path)
457
        print '%-50s %s' % (path, pat)
458
459
1 by mbp at sourcefrog
import from baz patch-364
460
def cmd_lookup_revision(revno):
461
    try:
462
        revno = int(revno)
463
    except ValueError:
464
        bailout("usage: lookup-revision REVNO",
465
                ["REVNO is a non-negative revision number for this branch"])
466
467
    print Branch('.').lookup_revision(revno) or NONE_STRING
468
469
470
471
def cmd_export(revno, dest):
472
    """Export past revision to destination directory."""
473
    b = Branch('.')
474
    rh = b.lookup_revision(int(revno))
475
    t = b.revision_tree(rh)
476
    t.export(dest)
477
478
479
480
######################################################################
481
# internal/test commands
482
483
484
def cmd_uuid():
485
    """Print a newly-generated UUID."""
63 by mbp at sourcefrog
fix up uuid command
486
    print bzrlib.osutils.uuid()
1 by mbp at sourcefrog
import from baz patch-364
487
488
489
8 by mbp at sourcefrog
store committer's timezone in revision and show
490
def cmd_local_time_offset():
491
    print bzrlib.osutils.local_time_offset()
492
493
494
57 by mbp at sourcefrog
error if --message is not given for commit
495
def cmd_commit(message=None, verbose=False):
97 by mbp at sourcefrog
- more commit help
496
    """Commit changes to a new revision.
497
498
--message MESSAGE
499
    Description of changes in this revision; free form text.
500
    It is recommended that the first line be a single-sentence
501
    summary.
502
--verbose
503
    Show status of changed files,
504
505
TODO: Commit only selected files.
506
507
TODO: Run hooks on tree to-be-committed, and after commit.
508
509
TODO: Strict commit that fails if there are unknown or deleted files.
510
"""
511
57 by mbp at sourcefrog
error if --message is not given for commit
512
    if not message:
513
        bailout("please specify a commit message")
1 by mbp at sourcefrog
import from baz patch-364
514
    Branch('.').commit(message, verbose=verbose)
515
516
113 by mbp at sourcefrog
More help for check command
517
def cmd_check(dir='.'):
518
    """check: Consistency check of branch history.
519
520
usage: bzr check [-v] [BRANCH]
521
522
options:
523
  --verbose, -v         Show progress of checking.
524
525
This command checks various invariants about the branch storage to
526
detect data corruption or bzr bugs.
527
"""
528
    import bzrlib.check
529
    bzrlib.check.check(Branch(dir, find_root=False))
1 by mbp at sourcefrog
import from baz patch-364
530
531
532
def cmd_is(pred, *rest):
533
    """Test whether PREDICATE is true."""
534
    try:
535
        cmd_handler = globals()['assert_' + pred.replace('-', '_')]
536
    except KeyError:
537
        bailout("unknown predicate: %s" % quotefn(pred))
538
        
539
    try:
540
        cmd_handler(*rest)
541
    except BzrCheckError:
542
        # by default we don't print the message so that this can
543
        # be used from shell scripts without producing noise
544
        sys.exit(1)
545
546
547
def cmd_username():
548
    print bzrlib.osutils.username()
549
550
551
def cmd_user_email():
552
    print bzrlib.osutils.user_email()
553
554
555
def cmd_gen_revision_id():
556
    import time
557
    print bzrlib.branch._gen_revision_id(time.time())
558
559
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
560
def cmd_selftest(verbose=False):
561
    """Run internal test suite"""
1 by mbp at sourcefrog
import from baz patch-364
562
    ## -v, if present, is seen by doctest; the argument is just here
563
    ## so our parser doesn't complain
564
565
    ## TODO: --verbose option
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
566
567
    failures, tests = 0, 0
1 by mbp at sourcefrog
import from baz patch-364
568
    
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
569
    import doctest, bzrlib.store, bzrlib.tests
1 by mbp at sourcefrog
import from baz patch-364
570
    bzrlib.trace.verbose = False
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
571
572
    for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
70 by mbp at sourcefrog
Prepare for smart recursive add.
573
        bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
574
        mf, mt = doctest.testmod(m)
575
        failures += mf
576
        tests += mt
577
        print '%-40s %3d tests' % (m.__name__, mt),
578
        if mf:
579
            print '%3d FAILED!' % mf
580
        else:
581
            print
582
583
    print '%-40s %3d tests' % ('total', tests),
584
    if failures:
585
        print '%3d FAILED!' % failures
586
    else:
587
        print
588
589
590
591
# deprecated
592
cmd_doctest = cmd_selftest
53 by mbp at sourcefrog
'selftest' command instead of 'doctest'
593
594
1 by mbp at sourcefrog
import from baz patch-364
595
######################################################################
596
# help
597
598
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
599
def cmd_help(topic=None):
600
    if topic == None:
601
        print __doc__
602
        return
603
604
    # otherwise, maybe the name of a command?
605
    try:
606
        cmdfn = globals()['cmd_' + topic.replace('-', '_')]
607
    except KeyError:
608
        bailout("no help for %r" % topic)
609
610
    doc = cmdfn.__doc__
611
    if doc == None:
612
        bailout("sorry, no detailed help yet for %r" % topic)
613
614
    print doc
615
        
616
1 by mbp at sourcefrog
import from baz patch-364
617
618
619
def cmd_version():
84 by mbp at sourcefrog
- update version string
620
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
621
    print bzrlib.__copyright__
1 by mbp at sourcefrog
import from baz patch-364
622
    print "http://bazaar-ng.org/"
623
    print
624
    print \
625
"""bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and
626
you may use, modify and redistribute it under the terms of the GNU 
627
General Public License version 2 or later."""
628
629
630
def cmd_rocks():
631
    """Statement of optimism."""
632
    print "it sure does!"
633
634
635
636
######################################################################
637
# main routine
638
639
640
# list of all available options; the rhs can be either None for an
641
# option that takes no argument, or a constructor function that checks
642
# the type.
643
OPTIONS = {
644
    'all':                    None,
645
    'help':                   None,
646
    'message':                unicode,
137 by mbp at sourcefrog
new --profile option
647
    'profile':                None,
1 by mbp at sourcefrog
import from baz patch-364
648
    'revision':               int,
649
    'show-ids':               None,
12 by mbp at sourcefrog
new --timezone option for bzr log
650
    'timezone':               str,
1 by mbp at sourcefrog
import from baz patch-364
651
    'verbose':                None,
652
    'version':                None,
653
    }
654
655
SHORT_OPTIONS = {
656
    'm':                      'message',
657
    'r':                      'revision',
658
    'v':                      'verbose',
659
}
660
661
# List of options that apply to particular commands; commands not
662
# listed take none.
663
cmd_options = {
664
    'add':                    ['verbose'],
665
    'commit':                 ['message', 'verbose'],
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
666
    'deleted':                ['show-ids'],
1 by mbp at sourcefrog
import from baz patch-364
667
    'diff':                   ['revision'],
668
    'inventory':              ['revision'],
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
669
    'log':                    ['timezone'],
1 by mbp at sourcefrog
import from baz patch-364
670
    'ls':                     ['revision', 'verbose'],
12 by mbp at sourcefrog
new --timezone option for bzr log
671
    'remove':                 ['verbose'],
1 by mbp at sourcefrog
import from baz patch-364
672
    'status':                 ['all'],
673
    }
674
675
676
cmd_args = {
677
    'add':                    ['file+'],
678
    'commit':                 [],
679
    'diff':                   [],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
680
    'export':                 ['revno', 'dest'],
1 by mbp at sourcefrog
import from baz patch-364
681
    'file-id':                ['filename'],
682
    'get-file-text':          ['text_id'],
683
    'get-inventory':          ['inventory_id'],
684
    'get-revision':           ['revision_id'],
685
    'get-revision-inventory': ['revision_id'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
686
    'help':                   ['topic?'],
687
    'init':                   [],
1 by mbp at sourcefrog
import from baz patch-364
688
    'log':                    [],
689
    'lookup-revision':        ['revno'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
690
    'relpath':                ['filename'],
1 by mbp at sourcefrog
import from baz patch-364
691
    'remove':                 ['file+'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
692
    'root':                   ['filename?'],
1 by mbp at sourcefrog
import from baz patch-364
693
    'status':                 [],
694
    }
695
696
697
def parse_args(argv):
698
    """Parse command line.
699
    
700
    Arguments and options are parsed at this level before being passed
701
    down to specific command handlers.  This routine knows, from a
702
    lookup table, something about the available options, what optargs
703
    they take, and which commands will accept them.
704
31 by Martin Pool
fix up parse_args doctest
705
    >>> parse_args('--help'.split())
1 by mbp at sourcefrog
import from baz patch-364
706
    ([], {'help': True})
31 by Martin Pool
fix up parse_args doctest
707
    >>> parse_args('--version'.split())
1 by mbp at sourcefrog
import from baz patch-364
708
    ([], {'version': True})
31 by Martin Pool
fix up parse_args doctest
709
    >>> parse_args('status --all'.split())
1 by mbp at sourcefrog
import from baz patch-364
710
    (['status'], {'all': True})
31 by Martin Pool
fix up parse_args doctest
711
    >>> parse_args('commit --message=biter'.split())
17 by mbp at sourcefrog
allow --option=ARG syntax
712
    (['commit'], {'message': u'biter'})
1 by mbp at sourcefrog
import from baz patch-364
713
    """
714
    args = []
715
    opts = {}
716
717
    # TODO: Maybe handle '--' to end options?
718
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
719
    while argv:
720
        a = argv.pop(0)
1 by mbp at sourcefrog
import from baz patch-364
721
        if a[0] == '-':
17 by mbp at sourcefrog
allow --option=ARG syntax
722
            optarg = None
1 by mbp at sourcefrog
import from baz patch-364
723
            if a[1] == '-':
724
                mutter("  got option %r" % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
725
                if '=' in a:
726
                    optname, optarg = a[2:].split('=', 1)
727
                else:
728
                    optname = a[2:]
1 by mbp at sourcefrog
import from baz patch-364
729
                if optname not in OPTIONS:
730
                    bailout('unknown long option %r' % a)
731
            else:
732
                shortopt = a[1:]
733
                if shortopt not in SHORT_OPTIONS:
734
                    bailout('unknown short option %r' % a)
735
                optname = SHORT_OPTIONS[shortopt]
736
            
737
            if optname in opts:
738
                # XXX: Do we ever want to support this, e.g. for -r?
739
                bailout('repeated option %r' % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
740
                
1 by mbp at sourcefrog
import from baz patch-364
741
            optargfn = OPTIONS[optname]
742
            if optargfn:
17 by mbp at sourcefrog
allow --option=ARG syntax
743
                if optarg == None:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
744
                    if not argv:
17 by mbp at sourcefrog
allow --option=ARG syntax
745
                        bailout('option %r needs an argument' % a)
746
                    else:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
747
                        optarg = argv.pop(0)
17 by mbp at sourcefrog
allow --option=ARG syntax
748
                opts[optname] = optargfn(optarg)
1 by mbp at sourcefrog
import from baz patch-364
749
            else:
17 by mbp at sourcefrog
allow --option=ARG syntax
750
                if optarg != None:
751
                    bailout('option %r takes no argument' % optname)
1 by mbp at sourcefrog
import from baz patch-364
752
                opts[optname] = True
753
        else:
754
            args.append(a)
755
756
    return args, opts
757
758
759
760
def _match_args(cmd, args):
761
    """Check non-option arguments match required pattern.
762
763
    >>> _match_args('status', ['asdasdsadasd'])
764
    Traceback (most recent call last):
765
    ...
766
    BzrError: ("extra arguments to command status: ['asdasdsadasd']", [])
767
    >>> _match_args('add', ['asdasdsadasd'])
768
    {'file_list': ['asdasdsadasd']}
769
    >>> _match_args('add', 'abc def gj'.split())
770
    {'file_list': ['abc', 'def', 'gj']}
771
    """
772
    # match argument pattern
773
    argform = cmd_args.get(cmd, [])
774
    argdict = {}
775
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
776
    # all but one.
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
777
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
778
    # step through args and argform, allowing appropriate 0-many matches
1 by mbp at sourcefrog
import from baz patch-364
779
    for ap in argform:
780
        argname = ap[:-1]
781
        if ap[-1] == '?':
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
782
            if args:
783
                argdict[argname] = args.pop(0)
1 by mbp at sourcefrog
import from baz patch-364
784
        elif ap[-1] == '*':
785
            assert 0
786
        elif ap[-1] == '+':
787
            if not args:
788
                bailout("command %r needs one or more %s"
789
                        % (cmd, argname.upper()))
790
            else:
791
                argdict[argname + '_list'] = args[:]
792
                args = []
793
        else:
794
            # just a plain arg
795
            argname = ap
796
            if not args:
797
                bailout("command %r requires argument %s"
798
                        % (cmd, argname.upper()))
799
            else:
800
                argdict[argname] = args.pop(0)
801
            
802
    if args:
803
        bailout("extra arguments to command %s: %r"
804
                % (cmd, args))
805
806
    return argdict
807
808
809
810
def run_bzr(argv):
811
    """Execute a command.
812
813
    This is similar to main(), but without all the trappings for
814
    logging and error handling.
815
    """
816
    try:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
817
        args, opts = parse_args(argv[1:])
1 by mbp at sourcefrog
import from baz patch-364
818
        if 'help' in opts:
819
            # TODO: pass down other arguments in case they asked for
820
            # help on a command name?
821
            cmd_help()
822
            return 0
823
        elif 'version' in opts:
824
            cmd_version()
825
            return 0
826
        cmd = args.pop(0)
827
    except IndexError:
828
        log_error('usage: bzr COMMAND\n')
829
        log_error('  try "bzr help"\n')
830
        return 1
115 by mbp at sourcefrog
todo
831
1 by mbp at sourcefrog
import from baz patch-364
832
    try:
833
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
834
    except KeyError:
835
        bailout("unknown command " + `cmd`)
836
137 by mbp at sourcefrog
new --profile option
837
    # global option
838
    if 'profile' in opts:
839
        profile = True
840
        del opts['profile']
841
    else:
842
        profile = False
1 by mbp at sourcefrog
import from baz patch-364
843
844
    # check options are reasonable
845
    allowed = cmd_options.get(cmd, [])
846
    for oname in opts:
847
        if oname not in allowed:
848
            bailout("option %r is not allowed for command %r"
849
                    % (oname, cmd))
850
137 by mbp at sourcefrog
new --profile option
851
    # mix arguments and options into one dictionary
1 by mbp at sourcefrog
import from baz patch-364
852
    cmdargs = _match_args(cmd, args)
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
853
    for k, v in opts.items():
854
        cmdargs[k.replace('-', '_')] = v
1 by mbp at sourcefrog
import from baz patch-364
855
137 by mbp at sourcefrog
new --profile option
856
    if profile:
857
        import hotshot
858
        prof = hotshot.Profile('.bzr.profile')
859
        ret = prof.runcall(cmd_handler, **cmdargs) or 0
860
        prof.close()
861
862
        import hotshot.stats
863
        stats = hotshot.stats.load('.bzr.profile')
864
        #stats.strip_dirs()
149 by mbp at sourcefrog
experiment with new nested inventory file format
865
        stats.sort_stats('time')
137 by mbp at sourcefrog
new --profile option
866
        stats.print_stats(20)
867
    else:
868
        return cmd_handler(**cmdargs) or 0
1 by mbp at sourcefrog
import from baz patch-364
869
870
871
872
def main(argv):
873
    ## TODO: Handle command-line options; probably know what options are valid for
874
    ## each command
875
876
    ## TODO: If the arguments are wrong, give a usage message rather
877
    ## than just a backtrace.
878
59 by mbp at sourcefrog
lift out tracefile creation code
879
    bzrlib.trace.create_tracefile(argv)
880
    
1 by mbp at sourcefrog
import from baz patch-364
881
    try:
882
        ret = run_bzr(argv)
883
        return ret
884
    except BzrError, e:
885
        log_error('bzr: error: ' + e.args[0] + '\n')
886
        if len(e.args) > 1:
887
            for h in e.args[1]:
888
                log_error('  ' + h + '\n')
889
        return 1
890
    except Exception, e:
891
        log_error('bzr: exception: %s\n' % e)
892
        log_error('    see .bzr.log for details\n')
893
        traceback.print_exc(None, bzrlib.trace._tracefile)
894
        traceback.print_exc(None, sys.stderr)
895
        return 1
896
897
    # TODO: Maybe nicer handling of IOError?
898
899
900
901
if __name__ == '__main__':
902
    sys.exit(main(sys.argv))
903
    ##import profile
904
    ##profile.run('main(sys.argv)')
905