~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzr.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-09 04:51:05 UTC
  • Revision ID: mbp@sourcefrog.net-20050309045105-d02cd410a115da2c
import all docs from arch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
 
 
3
 
1
4
# Copyright (C) 2004, 2005 by Martin Pool
2
5
# Copyright (C) 2005 by Canonical Ltd
3
6
 
25
28
* Metadata format is not stable yet -- you may need to
26
29
  discard history in the future.
27
30
 
 
31
* No handling of subdirectories, symlinks or any non-text files.
 
32
 
28
33
* Insufficient error handling.
29
34
 
30
35
* Many commands unimplemented or partially implemented.
35
40
 
36
41
Interesting commands::
37
42
 
38
 
  bzr help [COMMAND]
39
 
       Show help screen
 
43
  bzr help
 
44
       Show summary help screen
40
45
  bzr version
41
46
       Show software version/licence/non-warranty.
42
47
  bzr init
55
60
       Show summary of pending changes.
56
61
  bzr remove FILE...
57
62
       Make a file not versioned.
58
 
  bzr info
59
 
       Show statistics about this branch.
60
63
"""
61
64
 
62
 
 
 
65
# not currently working:
 
66
#  bzr check
 
67
#       Run internal consistency checks.
 
68
#  bzr info
 
69
#       Show some information about this branch.
 
70
 
 
71
 
 
72
 
 
73
__copyright__ = "Copyright 2005 Canonical Development Ltd."
 
74
__author__ = "Martin Pool <mbp@canonical.com>"
 
75
__docformat__ = "restructuredtext en"
 
76
__version__ = '0.0.0'
63
77
 
64
78
 
65
79
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
69
83
from pprint import pprint
70
84
from stat import *
71
85
from glob import glob
 
86
from ElementTree import Element, ElementTree, SubElement
72
87
 
73
88
import bzrlib
74
89
from bzrlib.store import ImmutableStore
101
116
## TODO: Perhaps make UUIDs predictable in test mode to make it easier
102
117
## to compare output?
103
118
 
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
 
 
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
 
 
 
119
## TODO: Is ElementTree really all that much better for our purposes?
 
120
## Perhaps using the standard MiniDOM would be enough?
 
121
 
 
122
 
 
123
 
 
124
 
 
125
 
 
126
 
 
127
######################################################################
 
128
# check status
119
129
 
120
130
 
121
131
def cmd_status(all=False):
137
147
    Branch('.').get_revision(revision_id).write_xml(sys.stdout)
138
148
 
139
149
 
 
150
def cmd_get_inventory(inventory_id):
 
151
    """Return inventory in XML by hash"""
 
152
    Branch('.').get_inventory(inventory_hash).write_xml(sys.stdout)
 
153
 
 
154
 
 
155
def cmd_get_revision_inventory(revision_id):
 
156
    """Output inventory for a revision."""
 
157
    b = Branch('.')
 
158
    b.get_revision_inventory(revision_id).write_xml(sys.stdout)
 
159
 
 
160
 
140
161
def cmd_get_file_text(text_id):
141
162
    """Get contents of a file by hash."""
142
163
    sf = Branch('.').text_store[text_id]
153
174
    print Branch('.').revno()
154
175
    
155
176
 
156
 
    
157
177
def cmd_add(file_list, verbose=False):
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.
 
178
    """Add specified files.
 
179
    
 
180
    Fails if the files are already added.
174
181
    """
175
 
    bzrlib.add.smart_add(file_list, verbose)
176
 
    
177
 
 
178
 
def cmd_relpath(filename):
179
 
    """Show path of file relative to root"""
180
 
    print Branch(filename).relpath(filename)
 
182
    Branch('.').add(file_list, verbose=verbose)
181
183
 
182
184
 
183
185
def cmd_inventory(revision=None):
196
198
 
197
199
 
198
200
def cmd_info():
199
 
    """info: Show statistical information for this branch
200
 
 
201
 
usage: bzr info"""
202
 
    import info
203
 
    info.show_info(Branch('.'))        
204
 
    
 
201
    b = Branch('.')
 
202
    print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
 
203
    print 'revision number:', b.revno()
 
204
    print 'number of versioned files:', len(b.read_working_inventory())
205
205
 
206
206
 
207
207
def cmd_remove(file_list, verbose=False):
208
 
    b = Branch(file_list[0])
209
 
    b.remove([b.relpath(f) for f in file_list], verbose=verbose)
 
208
    Branch('.').remove(file_list, verbose=verbose)
210
209
 
211
210
 
212
211
 
213
212
def cmd_file_id(filename):
214
 
    b = Branch(filename)
215
 
    i = b.inventory.path2id(b.relpath(filename))
 
213
    i = Branch('.').read_working_inventory().path2id(filename)
216
214
    if i is None:
217
215
        bailout("%s is not a versioned file" % filename)
218
216
    else:
247
245
 
248
246
 
249
247
def cmd_diff(revision=None):
250
 
    """bzr diff: Show differences in working tree.
251
 
    
252
 
usage: bzr diff [-r REV]
253
 
 
254
 
--revision REV
255
 
    Show changes since REV, rather than predecessor.
256
 
 
257
 
TODO: Given two revision arguments, show the difference between them.
258
 
 
259
 
TODO: Allow diff across branches.
260
 
 
261
 
TODO: Option to use external diff command; could be GNU diff, wdiff,
262
 
or a graphical diff.
263
 
 
264
 
TODO: Diff selected files.
265
 
"""
266
 
 
267
 
    ## TODO: Shouldn't be in the cmd function.
 
248
    """Show diff from basis to working copy.
 
249
 
 
250
    :todo: Take one or two revision arguments, look up those trees,
 
251
           and diff them.
 
252
 
 
253
    :todo: Allow diff across branches.
 
254
 
 
255
    :todo: Mangle filenames in diff to be more relevant.
 
256
 
 
257
    :todo: Shouldn't be in the cmd function.
 
258
    """
268
259
 
269
260
    b = Branch('.')
270
261
 
339
330
 
340
331
 
341
332
 
342
 
def cmd_deleted(show_ids=False):
343
 
    """List files deleted in the working tree.
344
 
 
345
 
TODO: Show files deleted since a previous revision, or between two revisions.
346
 
    """
347
 
    b = Branch('.')
348
 
    old = b.basis_tree()
349
 
    new = b.working_tree()
350
 
 
351
 
    for path, ie in old.inventory.iter_entries():
352
 
        if not new.has_id(ie.file_id):
353
 
            if show_ids:
354
 
                print '%-50s %s' % (path, ie.file_id)
355
 
            else:
356
 
                print path
357
 
                
358
 
    
359
 
 
360
 
 
361
 
def cmd_root(filename=None):
362
 
    """Print the branch root."""
363
 
    print bzrlib.branch.find_branch_root(filename)
364
 
    
365
 
 
366
 
def cmd_log(timezone='original'):
 
333
def cmd_log():
367
334
    """Show log of this branch.
368
335
 
369
336
    :todo: Options for utc; to show ids; to limit range; etc.
370
337
    """
371
 
    Branch('.').write_log(show_timezone=timezone)
 
338
    Branch('.').write_log()
372
339
 
373
340
 
374
341
def cmd_ls(revision=None, verbose=False):
403
370
        print quotefn(f)
404
371
 
405
372
 
406
 
 
407
 
def cmd_ignored(verbose=True):
408
 
    """List ignored files and the patterns that matched them.
409
 
      """
410
 
    tree = Branch('.').working_tree()
411
 
    for path, file_class, kind, id in tree.list_files():
412
 
        if file_class != 'I':
413
 
            continue
414
 
        ## XXX: Slightly inefficient since this was already calculated
415
 
        pat = tree.is_ignored(path)
416
 
        print '%-50s %s' % (path, pat)
417
 
 
418
 
 
419
373
def cmd_lookup_revision(revno):
420
374
    try:
421
375
        revno = int(revno)
442
396
 
443
397
def cmd_uuid():
444
398
    """Print a newly-generated UUID."""
445
 
    print bzrlib.osutils.uuid()
446
 
 
447
 
 
448
 
 
449
 
def cmd_local_time_offset():
450
 
    print bzrlib.osutils.local_time_offset()
451
 
 
452
 
 
453
 
 
454
 
def cmd_commit(message=None, verbose=False):
455
 
    """Commit changes to a new revision.
456
 
 
457
 
--message MESSAGE
458
 
    Description of changes in this revision; free form text.
459
 
    It is recommended that the first line be a single-sentence
460
 
    summary.
461
 
--verbose
462
 
    Show status of changed files,
463
 
 
464
 
TODO: Commit only selected files.
465
 
 
466
 
TODO: Run hooks on tree to-be-committed, and after commit.
467
 
 
468
 
TODO: Strict commit that fails if there are unknown or deleted files.
469
 
"""
470
 
 
471
 
    if not message:
472
 
        bailout("please specify a commit message")
 
399
    print uuid()
 
400
 
 
401
 
 
402
 
 
403
def cmd_commit(message, verbose=False):
473
404
    Branch('.').commit(message, verbose=verbose)
474
405
 
475
406
 
476
 
def cmd_check(dir='.'):
477
 
    """check: Consistency check of branch history.
478
 
 
479
 
usage: bzr check [-v] [BRANCH]
480
 
 
481
 
options:
482
 
  --verbose, -v         Show progress of checking.
483
 
 
484
 
This command checks various invariants about the branch storage to
485
 
detect data corruption or bzr bugs.
486
 
"""
487
 
    import bzrlib.check
488
 
    bzrlib.check.check(Branch(dir, find_root=False))
 
407
def cmd_check():
 
408
    """Check consistency of the branch."""
 
409
    check()
489
410
 
490
411
 
491
412
def cmd_is(pred, *rest):
516
437
    print bzrlib.branch._gen_revision_id(time.time())
517
438
 
518
439
 
519
 
def cmd_selftest(verbose=False):
520
 
    """Run internal test suite"""
 
440
def cmd_doctest():
 
441
    """Run internal doctest suite"""
521
442
    ## -v, if present, is seen by doctest; the argument is just here
522
443
    ## so our parser doesn't complain
523
444
 
524
445
    ## TODO: --verbose option
525
 
 
526
 
    failures, tests = 0, 0
527
446
    
528
 
    import doctest, bzrlib.store, bzrlib.tests
 
447
    import bzr, doctest, bzrlib.store
529
448
    bzrlib.trace.verbose = False
530
 
 
531
 
    for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
532
 
        bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
533
 
        mf, mt = doctest.testmod(m)
534
 
        failures += mf
535
 
        tests += mt
536
 
        print '%-40s %3d tests' % (m.__name__, mt),
537
 
        if mf:
538
 
            print '%3d FAILED!' % mf
539
 
        else:
540
 
            print
541
 
 
542
 
    print '%-40s %3d tests' % ('total', tests),
543
 
    if failures:
544
 
        print '%3d FAILED!' % failures
545
 
    else:
546
 
        print
547
 
 
548
 
 
549
 
 
550
 
# deprecated
551
 
cmd_doctest = cmd_selftest
 
449
    doctest.testmod(bzr)
 
450
    doctest.testmod(bzrlib.store)
 
451
    doctest.testmod(bzrlib.inventory)
 
452
    doctest.testmod(bzrlib.branch)
 
453
    doctest.testmod(bzrlib.osutils)
 
454
    doctest.testmod(bzrlib.tree)
 
455
 
 
456
    # more strenuous tests;
 
457
    import bzrlib.tests
 
458
    doctest.testmod(bzrlib.tests)
552
459
 
553
460
 
554
461
######################################################################
555
462
# help
556
463
 
557
464
 
558
 
def cmd_help(topic=None):
559
 
    if topic == None:
560
 
        print __doc__
561
 
        return
562
 
 
563
 
    # otherwise, maybe the name of a command?
564
 
    try:
565
 
        cmdfn = globals()['cmd_' + topic.replace('-', '_')]
566
 
    except KeyError:
567
 
        bailout("no help for %r" % topic)
568
 
 
569
 
    doc = cmdfn.__doc__
570
 
    if doc == None:
571
 
        bailout("sorry, no detailed help yet for %r" % topic)
572
 
 
573
 
    print doc
574
 
        
575
 
 
 
465
def cmd_help():
 
466
    # TODO: Specific help for particular commands
 
467
    print __doc__
576
468
 
577
469
 
578
470
def cmd_version():
579
 
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
580
 
    print bzrlib.__copyright__
 
471
    print "bzr (bazaar-ng) %s" % __version__
 
472
    print __copyright__
581
473
    print "http://bazaar-ng.org/"
582
474
    print
583
475
    print \
603
495
    'all':                    None,
604
496
    'help':                   None,
605
497
    'message':                unicode,
606
 
    'profile':                None,
607
498
    'revision':               int,
608
499
    'show-ids':               None,
609
 
    'timezone':               str,
610
500
    'verbose':                None,
611
501
    'version':                None,
612
502
    }
622
512
cmd_options = {
623
513
    'add':                    ['verbose'],
624
514
    'commit':                 ['message', 'verbose'],
625
 
    'deleted':                ['show-ids'],
626
515
    'diff':                   ['revision'],
627
516
    'inventory':              ['revision'],
628
 
    'log':                    ['timezone'],
629
517
    'ls':                     ['revision', 'verbose'],
 
518
    'status':                 ['all'],
 
519
    'log':                    ['show-ids'],
630
520
    'remove':                 ['verbose'],
631
 
    'status':                 ['all'],
632
521
    }
633
522
 
634
523
 
635
524
cmd_args = {
 
525
    'init':                   [],
636
526
    'add':                    ['file+'],
637
527
    'commit':                 [],
638
528
    'diff':                   [],
639
 
    'export':                 ['revno', 'dest'],
640
529
    'file-id':                ['filename'],
641
530
    'get-file-text':          ['text_id'],
642
531
    'get-inventory':          ['inventory_id'],
643
532
    'get-revision':           ['revision_id'],
644
533
    'get-revision-inventory': ['revision_id'],
645
 
    'help':                   ['topic?'],
646
 
    'init':                   [],
647
534
    'log':                    [],
648
535
    'lookup-revision':        ['revno'],
649
 
    'relpath':                ['filename'],
 
536
    'export':                 ['revno', 'dest'],
650
537
    'remove':                 ['file+'],
651
 
    'root':                   ['filename?'],
652
538
    'status':                 [],
653
539
    }
654
540
 
661
547
    lookup table, something about the available options, what optargs
662
548
    they take, and which commands will accept them.
663
549
 
664
 
    >>> parse_args('--help'.split())
 
550
    >>> parse_args('bzr --help'.split())
665
551
    ([], {'help': True})
666
 
    >>> parse_args('--version'.split())
 
552
    >>> parse_args('bzr --version'.split())
667
553
    ([], {'version': True})
668
 
    >>> parse_args('status --all'.split())
 
554
    >>> parse_args('bzr status --all'.split())
669
555
    (['status'], {'all': True})
670
 
    >>> parse_args('commit --message=biter'.split())
671
 
    (['commit'], {'message': u'biter'})
672
556
    """
673
557
    args = []
674
558
    opts = {}
675
559
 
676
560
    # TODO: Maybe handle '--' to end options?
677
561
 
678
 
    while argv:
679
 
        a = argv.pop(0)
 
562
    it = iter(argv[1:])
 
563
    while it:
 
564
        a = it.next()
680
565
        if a[0] == '-':
681
 
            optarg = None
682
566
            if a[1] == '-':
683
567
                mutter("  got option %r" % a)
684
 
                if '=' in a:
685
 
                    optname, optarg = a[2:].split('=', 1)
686
 
                else:
687
 
                    optname = a[2:]
 
568
                optname = a[2:]
688
569
                if optname not in OPTIONS:
689
570
                    bailout('unknown long option %r' % a)
690
571
            else:
696
577
            if optname in opts:
697
578
                # XXX: Do we ever want to support this, e.g. for -r?
698
579
                bailout('repeated option %r' % a)
699
 
                
700
580
            optargfn = OPTIONS[optname]
701
581
            if optargfn:
702
 
                if optarg == None:
703
 
                    if not argv:
704
 
                        bailout('option %r needs an argument' % a)
705
 
                    else:
706
 
                        optarg = argv.pop(0)
707
 
                opts[optname] = optargfn(optarg)
 
582
                if not it:
 
583
                    bailout('option %r needs an argument' % a)
 
584
                opts[optname] = optargfn(it.next())
 
585
                mutter("    option argument %r" % opts[optname])
708
586
            else:
709
 
                if optarg != None:
710
 
                    bailout('option %r takes no argument' % optname)
 
587
                # takes no option argument
711
588
                opts[optname] = True
 
589
        elif a[:1] == '-':
 
590
            bailout('unknown short option %r' % a)
712
591
        else:
713
592
            args.append(a)
714
593
 
733
612
    argdict = {}
734
613
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
735
614
    # all but one.
736
 
 
737
 
    # step through args and argform, allowing appropriate 0-many matches
738
615
    for ap in argform:
739
616
        argname = ap[:-1]
740
617
        if ap[-1] == '?':
741
 
            if args:
742
 
                argdict[argname] = args.pop(0)
 
618
            assert 0
743
619
        elif ap[-1] == '*':
744
620
            assert 0
745
621
        elif ap[-1] == '+':
773
649
    logging and error handling.
774
650
    """
775
651
    try:
776
 
        args, opts = parse_args(argv[1:])
 
652
        args, opts = parse_args(argv)
777
653
        if 'help' in opts:
778
654
            # TODO: pass down other arguments in case they asked for
779
655
            # help on a command name?
787
663
        log_error('usage: bzr COMMAND\n')
788
664
        log_error('  try "bzr help"\n')
789
665
        return 1
790
 
 
 
666
            
791
667
    try:
792
668
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
793
669
    except KeyError:
794
670
        bailout("unknown command " + `cmd`)
795
671
 
796
 
    # global option
797
 
    if 'profile' in opts:
798
 
        profile = True
799
 
        del opts['profile']
800
 
    else:
801
 
        profile = False
 
672
    # TODO: special --profile option to turn on the Python profiler
802
673
 
803
674
    # check options are reasonable
804
675
    allowed = cmd_options.get(cmd, [])
807
678
            bailout("option %r is not allowed for command %r"
808
679
                    % (oname, cmd))
809
680
 
810
 
    # mix arguments and options into one dictionary
811
681
    cmdargs = _match_args(cmd, args)
812
 
    for k, v in opts.items():
813
 
        cmdargs[k.replace('-', '_')] = v
814
 
 
815
 
    if profile:
816
 
        import hotshot
817
 
        prof = hotshot.Profile('.bzr.profile')
818
 
        ret = prof.runcall(cmd_handler, **cmdargs) or 0
819
 
        prof.close()
820
 
 
821
 
        import hotshot.stats
822
 
        stats = hotshot.stats.load('.bzr.profile')
823
 
        #stats.strip_dirs()
824
 
        stats.sort_stats('cumulative', 'calls')
825
 
        stats.print_stats(20)
826
 
    else:
827
 
        return cmd_handler(**cmdargs) or 0
 
682
    cmdargs.update(opts)
 
683
 
 
684
    ret = cmd_handler(**cmdargs) or 0
828
685
 
829
686
 
830
687
 
835
692
    ## TODO: If the arguments are wrong, give a usage message rather
836
693
    ## than just a backtrace.
837
694
 
838
 
    bzrlib.trace.create_tracefile(argv)
839
 
    
840
695
    try:
 
696
        t = bzrlib.trace._tracefile
 
697
        t.write('-' * 60 + '\n')
 
698
        t.write('bzr invoked at %s\n' % format_date(time.time()))
 
699
        t.write('  by %s on %s\n' % (bzrlib.osutils.username(), socket.gethostname()))
 
700
        t.write('  arguments: %r\n' % argv)
 
701
 
 
702
        starttime = os.times()[4]
 
703
 
 
704
        import platform
 
705
        t.write('  platform: %s\n' % platform.platform())
 
706
        t.write('  python: %s\n' % platform.python_version())
 
707
 
841
708
        ret = run_bzr(argv)
 
709
        
 
710
        times = os.times()
 
711
        mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
 
712
               % times[:4])
 
713
        mutter("    %.3f elapsed" % (times[4] - starttime))
 
714
 
842
715
        return ret
843
716
    except BzrError, e:
844
717
        log_error('bzr: error: ' + e.args[0] + '\n')