~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-30 22:27:17 UTC
  • Revision ID: mbp@sourcefrog.net-20050330222717-027b5837127b938d
experiment with new nested inventory file format
not used by default yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python
2
 
 
3
 
 
4
1
# Copyright (C) 2004, 2005 by Martin Pool
5
2
# Copyright (C) 2005 by Canonical Ltd
6
3
 
28
25
* Metadata format is not stable yet -- you may need to
29
26
  discard history in the future.
30
27
 
31
 
* No handling of subdirectories, symlinks or any non-text files.
32
 
 
33
28
* Insufficient error handling.
34
29
 
35
30
* Many commands unimplemented or partially implemented.
40
35
 
41
36
Interesting commands::
42
37
 
43
 
  bzr help
44
 
       Show summary help screen
 
38
  bzr help [COMMAND]
 
39
       Show help screen
45
40
  bzr version
46
41
       Show software version/licence/non-warranty.
47
42
  bzr init
60
55
       Show summary of pending changes.
61
56
  bzr remove FILE...
62
57
       Make a file not versioned.
 
58
  bzr info
 
59
       Show statistics about this branch.
63
60
"""
64
61
 
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'
 
62
 
75
63
 
76
64
 
77
65
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
165
153
    print Branch('.').revno()
166
154
    
167
155
 
 
156
    
168
157
def cmd_add(file_list, verbose=False):
169
 
    """Add specified files.
 
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
    """
 
175
    bzrlib.add.smart_add(file_list, verbose)
170
176
    
171
 
    Fails if the files are already added.
172
 
    """
173
 
    Branch('.').add(file_list, verbose=verbose)
 
177
 
 
178
def cmd_relpath(filename):
 
179
    """Show path of file relative to root"""
 
180
    print Branch(filename).relpath(filename)
174
181
 
175
182
 
176
183
def cmd_inventory(revision=None):
189
196
 
190
197
 
191
198
def cmd_info():
192
 
    b = Branch('.')
193
 
    print 'branch format:', b.controlfile('branch-format', 'r').readline().rstrip('\n')
194
 
 
195
 
    def plural(n, base='', pl=None):
196
 
        if n == 1:
197
 
            return base
198
 
        elif pl is not None:
199
 
            return pl
200
 
        else:
201
 
            return 's'
202
 
 
203
 
    count_version_dirs = 0
204
 
 
205
 
    count_status = {'A': 0, 'D': 0, 'M': 0, 'R': 0, '?': 0, 'I': 0, '.': 0}
206
 
    for st_tup in bzrlib.diff_trees(b.basis_tree(), b.working_tree()):
207
 
        fs = st_tup[0]
208
 
        count_status[fs] += 1
209
 
        if fs not in ['I', '?'] and st_tup[4] == 'directory':
210
 
            count_version_dirs += 1
211
 
 
212
 
    print
213
 
    print 'in the working tree:'
214
 
    for name, fs in (('unchanged', '.'),
215
 
                     ('modified', 'M'), ('added', 'A'), ('removed', 'D'),
216
 
                     ('renamed', 'R'), ('unknown', '?'), ('ignored', 'I'),
217
 
                     ):
218
 
        print '  %5d %s' % (count_status[fs], name)
219
 
    print '  %5d versioned subdirector%s' % (count_version_dirs,
220
 
                                             plural(count_version_dirs, 'y', 'ies'))
221
 
 
222
 
    print
223
 
    print 'branch history:'
224
 
    history = b.revision_history()
225
 
    revno = len(history)
226
 
    print '  %5d revision%s' % (revno, plural(revno))
227
 
    committers = Set()
228
 
    for rev in history:
229
 
        committers.add(b.get_revision(rev).committer)
230
 
    print '  %5d committer%s' % (len(committers), plural(len(committers)))
231
 
    if revno > 0:
232
 
        firstrev = b.get_revision(history[0])
233
 
        age = int((time.time() - firstrev.timestamp) / 3600 / 24)
234
 
        print '  %5d day%s old' % (age, plural(age))
235
 
        print '  first revision: %s' % format_date(firstrev.timestamp,
236
 
                                                 firstrev.timezone)
237
 
 
238
 
        lastrev = b.get_revision(history[-1])
239
 
        print '  latest revision: %s' % format_date(lastrev.timestamp,
240
 
                                                    lastrev.timezone)
241
 
        
 
199
    """info: Show statistical information for this branch
 
200
 
 
201
usage: bzr info"""
 
202
    import info
 
203
    info.show_info(Branch('.'))        
242
204
    
243
205
 
244
206
 
245
207
def cmd_remove(file_list, verbose=False):
246
 
    Branch('.').remove(file_list, verbose=verbose)
 
208
    b = Branch(file_list[0])
 
209
    b.remove([b.relpath(f) for f in file_list], verbose=verbose)
247
210
 
248
211
 
249
212
 
250
213
def cmd_file_id(filename):
251
 
    i = Branch('.').read_working_inventory().path2id(filename)
 
214
    b = Branch(filename)
 
215
    i = b.inventory.path2id(b.relpath(filename))
252
216
    if i is None:
253
217
        bailout("%s is not a versioned file" % filename)
254
218
    else:
283
247
 
284
248
 
285
249
def cmd_diff(revision=None):
286
 
    """Show diff from basis to working copy.
287
 
 
288
 
    :todo: Take one or two revision arguments, look up those trees,
289
 
           and diff them.
290
 
 
291
 
    :todo: Allow diff across branches.
292
 
 
293
 
    :todo: Mangle filenames in diff to be more relevant.
294
 
 
295
 
    :todo: Shouldn't be in the cmd function.
296
 
    """
 
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.
297
268
 
298
269
    b = Branch('.')
299
270
 
368
339
 
369
340
 
370
341
 
 
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
    ## TODO: Much more efficient way to do this: read in new
 
352
    ## directories with readdir, rather than stating each one.  Same
 
353
    ## level of effort but possibly much less IO.  (Or possibly not,
 
354
    ## if the directories are very large...)
 
355
 
 
356
    for path, ie in old.inventory.iter_entries():
 
357
        if not new.has_id(ie.file_id):
 
358
            if show_ids:
 
359
                print '%-50s %s' % (path, ie.file_id)
 
360
            else:
 
361
                print path
 
362
 
 
363
 
 
364
 
 
365
def cmd_parse_inventory():
 
366
    import cElementTree
 
367
    
 
368
    cElementTree.ElementTree().parse(file('.bzr/inventory'))
 
369
 
 
370
 
 
371
 
 
372
def cmd_load_inventory():
 
373
    inv = Branch('.').basis_tree().inventory
 
374
 
 
375
 
 
376
 
 
377
def cmd_dump_new_inventory():
 
378
    import bzrlib.newinventory
 
379
    inv = Branch('.').basis_tree().inventory
 
380
    bzrlib.newinventory.write_inventory(inv, sys.stdout)
 
381
                
 
382
    
 
383
def cmd_dump_slacker_inventory():
 
384
    import bzrlib.newinventory
 
385
    inv = Branch('.').basis_tree().inventory
 
386
    bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
 
387
                
 
388
    
 
389
 
 
390
def cmd_root(filename=None):
 
391
    """Print the branch root."""
 
392
    print bzrlib.branch.find_branch_root(filename)
 
393
    
 
394
 
371
395
def cmd_log(timezone='original'):
372
396
    """Show log of this branch.
373
397
 
408
432
        print quotefn(f)
409
433
 
410
434
 
 
435
 
 
436
def cmd_ignored(verbose=True):
 
437
    """List ignored files and the patterns that matched them.
 
438
      """
 
439
    tree = Branch('.').working_tree()
 
440
    for path, file_class, kind, id in tree.list_files():
 
441
        if file_class != 'I':
 
442
            continue
 
443
        ## XXX: Slightly inefficient since this was already calculated
 
444
        pat = tree.is_ignored(path)
 
445
        print '%-50s %s' % (path, pat)
 
446
 
 
447
 
411
448
def cmd_lookup_revision(revno):
412
449
    try:
413
450
        revno = int(revno)
434
471
 
435
472
def cmd_uuid():
436
473
    """Print a newly-generated UUID."""
437
 
    print uuid()
 
474
    print bzrlib.osutils.uuid()
438
475
 
439
476
 
440
477
 
443
480
 
444
481
 
445
482
 
446
 
def cmd_commit(message, verbose=False):
 
483
def cmd_commit(message=None, verbose=False):
 
484
    """Commit changes to a new revision.
 
485
 
 
486
--message MESSAGE
 
487
    Description of changes in this revision; free form text.
 
488
    It is recommended that the first line be a single-sentence
 
489
    summary.
 
490
--verbose
 
491
    Show status of changed files,
 
492
 
 
493
TODO: Commit only selected files.
 
494
 
 
495
TODO: Run hooks on tree to-be-committed, and after commit.
 
496
 
 
497
TODO: Strict commit that fails if there are unknown or deleted files.
 
498
"""
 
499
 
 
500
    if not message:
 
501
        bailout("please specify a commit message")
447
502
    Branch('.').commit(message, verbose=verbose)
448
503
 
449
504
 
450
 
def cmd_check():
451
 
    """Check consistency of the branch."""
452
 
    check()
 
505
def cmd_check(dir='.'):
 
506
    """check: Consistency check of branch history.
 
507
 
 
508
usage: bzr check [-v] [BRANCH]
 
509
 
 
510
options:
 
511
  --verbose, -v         Show progress of checking.
 
512
 
 
513
This command checks various invariants about the branch storage to
 
514
detect data corruption or bzr bugs.
 
515
"""
 
516
    import bzrlib.check
 
517
    bzrlib.check.check(Branch(dir, find_root=False))
453
518
 
454
519
 
455
520
def cmd_is(pred, *rest):
480
545
    print bzrlib.branch._gen_revision_id(time.time())
481
546
 
482
547
 
483
 
def cmd_doctest():
484
 
    """Run internal doctest suite"""
 
548
def cmd_selftest(verbose=False):
 
549
    """Run internal test suite"""
485
550
    ## -v, if present, is seen by doctest; the argument is just here
486
551
    ## so our parser doesn't complain
487
552
 
488
553
    ## TODO: --verbose option
 
554
 
 
555
    failures, tests = 0, 0
489
556
    
490
 
    import doctest, bzrlib.store
 
557
    import doctest, bzrlib.store, bzrlib.tests
491
558
    bzrlib.trace.verbose = False
492
 
    doctest.testmod(bzrlib.store)
493
 
    doctest.testmod(bzrlib.inventory)
494
 
    doctest.testmod(bzrlib.branch)
495
 
    doctest.testmod(bzrlib.osutils)
496
 
    doctest.testmod(bzrlib.tree)
497
 
 
498
 
    # more strenuous tests;
499
 
    import bzrlib.tests
500
 
    doctest.testmod(bzrlib.tests)
501
 
 
502
 
 
503
 
cmd_selftest = cmd_doctest
 
559
 
 
560
    for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
 
561
        bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
 
562
        mf, mt = doctest.testmod(m)
 
563
        failures += mf
 
564
        tests += mt
 
565
        print '%-40s %3d tests' % (m.__name__, mt),
 
566
        if mf:
 
567
            print '%3d FAILED!' % mf
 
568
        else:
 
569
            print
 
570
 
 
571
    print '%-40s %3d tests' % ('total', tests),
 
572
    if failures:
 
573
        print '%3d FAILED!' % failures
 
574
    else:
 
575
        print
 
576
 
 
577
 
 
578
 
 
579
# deprecated
 
580
cmd_doctest = cmd_selftest
504
581
 
505
582
 
506
583
######################################################################
507
584
# help
508
585
 
509
586
 
510
 
def cmd_help():
511
 
    # TODO: Specific help for particular commands
512
 
    print __doc__
 
587
def cmd_help(topic=None):
 
588
    if topic == None:
 
589
        print __doc__
 
590
        return
 
591
 
 
592
    # otherwise, maybe the name of a command?
 
593
    try:
 
594
        cmdfn = globals()['cmd_' + topic.replace('-', '_')]
 
595
    except KeyError:
 
596
        bailout("no help for %r" % topic)
 
597
 
 
598
    doc = cmdfn.__doc__
 
599
    if doc == None:
 
600
        bailout("sorry, no detailed help yet for %r" % topic)
 
601
 
 
602
    print doc
 
603
        
 
604
 
513
605
 
514
606
 
515
607
def cmd_version():
516
 
    print "bzr (bazaar-ng) %s" % __version__
517
 
    print __copyright__
 
608
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
 
609
    print bzrlib.__copyright__
518
610
    print "http://bazaar-ng.org/"
519
611
    print
520
612
    print \
540
632
    'all':                    None,
541
633
    'help':                   None,
542
634
    'message':                unicode,
 
635
    'profile':                None,
543
636
    'revision':               int,
544
637
    'show-ids':               None,
545
638
    'timezone':               str,
558
651
cmd_options = {
559
652
    'add':                    ['verbose'],
560
653
    'commit':                 ['message', 'verbose'],
 
654
    'deleted':                ['show-ids'],
561
655
    'diff':                   ['revision'],
562
656
    'inventory':              ['revision'],
563
 
    'log':                    ['show-ids', 'timezone'],
 
657
    'log':                    ['timezone'],
564
658
    'ls':                     ['revision', 'verbose'],
565
659
    'remove':                 ['verbose'],
566
660
    'status':                 ['all'],
568
662
 
569
663
 
570
664
cmd_args = {
571
 
    'init':                   [],
572
665
    'add':                    ['file+'],
573
666
    'commit':                 [],
574
667
    'diff':                   [],
 
668
    'export':                 ['revno', 'dest'],
575
669
    'file-id':                ['filename'],
576
670
    'get-file-text':          ['text_id'],
577
671
    'get-inventory':          ['inventory_id'],
578
672
    'get-revision':           ['revision_id'],
579
673
    'get-revision-inventory': ['revision_id'],
 
674
    'help':                   ['topic?'],
 
675
    'init':                   [],
580
676
    'log':                    [],
581
677
    'lookup-revision':        ['revno'],
582
 
    'export':                 ['revno', 'dest'],
 
678
    'relpath':                ['filename'],
583
679
    'remove':                 ['file+'],
 
680
    'root':                   ['filename?'],
584
681
    'status':                 [],
585
682
    }
586
683
 
637
734
                    else:
638
735
                        optarg = argv.pop(0)
639
736
                opts[optname] = optargfn(optarg)
640
 
                mutter("    option argument %r" % opts[optname])
641
737
            else:
642
738
                if optarg != None:
643
739
                    bailout('option %r takes no argument' % optname)
667
763
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
668
764
    # all but one.
669
765
 
 
766
    # step through args and argform, allowing appropriate 0-many matches
670
767
    for ap in argform:
671
768
        argname = ap[:-1]
672
769
        if ap[-1] == '?':
673
 
            assert 0
 
770
            if args:
 
771
                argdict[argname] = args.pop(0)
674
772
        elif ap[-1] == '*':
675
773
            assert 0
676
774
        elif ap[-1] == '+':
718
816
        log_error('usage: bzr COMMAND\n')
719
817
        log_error('  try "bzr help"\n')
720
818
        return 1
721
 
            
 
819
 
722
820
    try:
723
821
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
724
822
    except KeyError:
725
823
        bailout("unknown command " + `cmd`)
726
824
 
727
 
    # TODO: special --profile option to turn on the Python profiler
 
825
    # global option
 
826
    if 'profile' in opts:
 
827
        profile = True
 
828
        del opts['profile']
 
829
    else:
 
830
        profile = False
728
831
 
729
832
    # check options are reasonable
730
833
    allowed = cmd_options.get(cmd, [])
733
836
            bailout("option %r is not allowed for command %r"
734
837
                    % (oname, cmd))
735
838
 
 
839
    # mix arguments and options into one dictionary
736
840
    cmdargs = _match_args(cmd, args)
737
 
    cmdargs.update(opts)
738
 
 
739
 
    ret = cmd_handler(**cmdargs) or 0
 
841
    for k, v in opts.items():
 
842
        cmdargs[k.replace('-', '_')] = v
 
843
 
 
844
    if profile:
 
845
        import hotshot
 
846
        prof = hotshot.Profile('.bzr.profile')
 
847
        ret = prof.runcall(cmd_handler, **cmdargs) or 0
 
848
        prof.close()
 
849
 
 
850
        import hotshot.stats
 
851
        stats = hotshot.stats.load('.bzr.profile')
 
852
        #stats.strip_dirs()
 
853
        stats.sort_stats('time')
 
854
        stats.print_stats(20)
 
855
    else:
 
856
        return cmd_handler(**cmdargs) or 0
740
857
 
741
858
 
742
859
 
747
864
    ## TODO: If the arguments are wrong, give a usage message rather
748
865
    ## than just a backtrace.
749
866
 
 
867
    bzrlib.trace.create_tracefile(argv)
 
868
    
750
869
    try:
751
 
        # TODO: Lift into separate function in trace.py
752
 
        # TODO: Also show contents of /etc/lsb-release, if it can be parsed.
753
 
        #       Perhaps that should eventually go into the platform library?
754
 
        # TODO: If the file doesn't exist, add a note describing it.
755
 
        t = bzrlib.trace._tracefile
756
 
        t.write('-' * 60 + '\n')
757
 
        t.write('bzr invoked at %s\n' % format_date(time.time()))
758
 
        t.write('  by %s on %s\n' % (bzrlib.osutils.username(), socket.getfqdn()))
759
 
        t.write('  arguments: %r\n' % argv)
760
 
 
761
 
        starttime = os.times()[4]
762
 
 
763
 
        import platform
764
 
        t.write('  platform: %s\n' % platform.platform())
765
 
        t.write('  python: %s\n' % platform.python_version())
766
 
 
767
870
        ret = run_bzr(argv)
768
 
        
769
 
        times = os.times()
770
 
        mutter("finished, %.3fu/%.3fs cpu, %.3fu/%.3fs cum"
771
 
               % times[:4])
772
 
        mutter("    %.3f elapsed" % (times[4] - starttime))
773
 
 
774
871
        return ret
775
872
    except BzrError, e:
776
873
        log_error('bzr: error: ' + e.args[0] + '\n')