~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: mbp at sourcefrog
  • Date: 2005-04-09 06:21:44 UTC
  • Revision ID: mbp@sourcefrog.net-20050409062144-e47a4b64106e4c21af99beaf
debugĀ output

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
       Make files versioned.
46
46
  bzr log
47
47
       Show revision history.
48
 
  bzr diff
 
48
  bzr diff [FILE...]
49
49
       Show changes from last revision to working copy.
50
50
  bzr commit -m 'MESSAGE'
51
51
       Store current state as new revision.
62
62
 
63
63
 
64
64
 
65
 
import sys, os, random, time, sha, sets, types, re, shutil, tempfile
66
 
import traceback, socket, fnmatch, difflib
67
 
from os import path
 
65
import sys, os, time, types, shutil, tempfile, traceback, fnmatch, difflib, os.path
68
66
from sets import Set
69
67
from pprint import pprint
70
68
from stat import *
73
71
import bzrlib
74
72
from bzrlib.store import ImmutableStore
75
73
from bzrlib.trace import mutter, note, log_error
76
 
from bzrlib.errors import bailout, BzrError
 
74
from bzrlib.errors import bailout, BzrError, BzrCheckError
77
75
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
78
76
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
79
77
from bzrlib.revision import Revision
180
178
    print Branch(filename).relpath(filename)
181
179
 
182
180
 
 
181
 
183
182
def cmd_inventory(revision=None):
184
183
    """Show inventory of the current working copy."""
185
184
    ## TODO: Also optionally show a previous inventory
195
194
 
196
195
 
197
196
 
 
197
# TODO: Maybe a 'mv' command that has the combined move/rename
 
198
# special behaviour of Unix?
 
199
 
 
200
def cmd_move(source_list, dest):
 
201
    b = Branch('.')
 
202
 
 
203
    b.move([b.relpath(s) for s in source_list], b.relpath(dest))
 
204
 
 
205
 
 
206
 
 
207
def cmd_rename(from_name, to_name):
 
208
    """Change the name of an entry.
 
209
 
 
210
usage: bzr rename FROM_NAME TO_NAME
 
211
 
 
212
examples:
 
213
  bzr rename frob.c frobber.c
 
214
  bzr rename src/frob.c lib/frob.c
 
215
 
 
216
It is an error if the destination name exists.
 
217
 
 
218
See also the 'move' command, which moves files into a different
 
219
directory without changing their name.
 
220
 
 
221
TODO: Some way to rename multiple files without invoking bzr for each
 
222
one?"""
 
223
    b = Branch('.')
 
224
    b.rename_one(b.relpath(from_name), b.relpath(to_name))
 
225
    
 
226
 
 
227
 
 
228
 
 
229
def cmd_renames(dir='.'):
 
230
    """Show list of renamed files.
 
231
 
 
232
usage: bzr renames [BRANCH]
 
233
 
 
234
TODO: Option to show renames between two historical versions.
 
235
 
 
236
TODO: Only show renames under dir, rather than in the whole branch.
 
237
"""
 
238
    b = Branch(dir)
 
239
    old_inv = b.basis_tree().inventory
 
240
    new_inv = b.read_working_inventory()
 
241
    
 
242
    renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
 
243
    renames.sort()
 
244
    for old_name, new_name in renames:
 
245
        print "%s => %s" % (old_name, new_name)        
 
246
 
 
247
 
 
248
 
198
249
def cmd_info():
199
250
    """info: Show statistical information for this branch
200
251
 
211
262
 
212
263
 
213
264
def cmd_file_id(filename):
 
265
    """Print file_id of a particular file or directory.
 
266
 
 
267
usage: bzr file-id FILE
 
268
 
 
269
The file_id is assigned when the file is first added and remains the
 
270
same through all revisions where the file exists, even when it is
 
271
moved or renamed.
 
272
"""
214
273
    b = Branch(filename)
215
274
    i = b.inventory.path2id(b.relpath(filename))
216
 
    if i is None:
217
 
        bailout("%s is not a versioned file" % filename)
 
275
    if i == None:
 
276
        bailout("%r is not a versioned file" % filename)
218
277
    else:
219
278
        print i
220
279
 
221
280
 
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
 
281
def cmd_file_id_path(filename):
 
282
    """Print path of file_ids to a file or directory.
 
283
 
 
284
usage: bzr file-id-path FILE
 
285
 
 
286
This prints one line for each directory down to the target,
 
287
starting at the branch root."""
 
288
    b = Branch(filename)
 
289
    inv = b.inventory
 
290
    fid = inv.path2id(b.relpath(filename))
 
291
    if fid == None:
 
292
        bailout("%r is not a versioned file" % filename)
 
293
    for fip in inv.get_idpath(fid):
 
294
        print fip
228
295
 
229
296
 
230
297
def cmd_revision_history():
232
299
        print patchid
233
300
 
234
301
 
 
302
def cmd_directories():
 
303
    for name, ie in Branch('.').read_working_inventory().directories():
 
304
        if name == '':
 
305
            print '.'
 
306
        else:
 
307
            print name
 
308
 
 
309
 
 
310
def cmd_missing():
 
311
    for name, ie in Branch('.').working_tree().missing():
 
312
        print name
 
313
 
235
314
 
236
315
def cmd_init():
237
316
    # TODO: Check we're not already in a working directory?  At the
246
325
    Branch('.', init=True)
247
326
 
248
327
 
249
 
def cmd_diff(revision=None):
 
328
def cmd_diff(revision=None, file_list=None):
250
329
    """bzr diff: Show differences in working tree.
251
330
    
252
 
usage: bzr diff [-r REV]
 
331
usage: bzr diff [-r REV] [FILE...]
253
332
 
254
333
--revision REV
255
334
    Show changes since REV, rather than predecessor.
256
335
 
 
336
If files are listed, only the changes in those files are listed.
 
337
Otherwise, all changes for the tree are listed.
 
338
 
257
339
TODO: Given two revision arguments, show the difference between them.
258
340
 
259
341
TODO: Allow diff across branches.
261
343
TODO: Option to use external diff command; could be GNU diff, wdiff,
262
344
or a graphical diff.
263
345
 
264
 
TODO: Diff selected files.
 
346
TODO: If a directory is given, diff everything under that.
 
347
 
 
348
TODO: Selected-file diff is inefficient and doesn't show you deleted files.
265
349
"""
266
350
 
267
351
    ## TODO: Shouldn't be in the cmd function.
274
358
        old_tree = b.revision_tree(b.lookup_revision(revision))
275
359
        
276
360
    new_tree = b.working_tree()
277
 
    old_inv = old_tree.inventory
278
 
    new_inv = new_tree.inventory
279
361
 
280
362
    # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
281
363
    old_label = ''
290
372
    # be usefully made into a much faster special case.
291
373
 
292
374
    # TODO: Better to return them in sorted order I think.
 
375
 
 
376
    # FIXME: If given a file list, compare only those files rather
 
377
    # than comparing everything and then throwing stuff away.
293
378
    
294
379
    for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
295
 
        d = None
296
380
 
 
381
        if file_list and new_name not in file_list:
 
382
            continue
 
383
        
297
384
        # Don't show this by default; maybe do it if an option is passed
298
385
        # idlabel = '      {%s}' % fid
299
386
        idlabel = ''
301
388
        # FIXME: Something about the diff format makes patch unhappy
302
389
        # with newly-added files.
303
390
 
304
 
        def diffit(*a, **kw):
305
 
            sys.stdout.writelines(difflib.unified_diff(*a, **kw))
 
391
        def diffit(oldlines, newlines, **kw):
 
392
            # FIXME: difflib is wrong if there is no trailing newline.
 
393
 
 
394
            # Special workaround for Python2.3, where difflib fails if
 
395
            # both sequences are empty.
 
396
            if oldlines or newlines:
 
397
                sys.stdout.writelines(difflib.unified_diff(oldlines, newlines, **kw))
306
398
            print
307
399
        
308
400
        if file_state in ['.', '?', 'I']:
339
431
 
340
432
 
341
433
 
 
434
def cmd_deleted(show_ids=False):
 
435
    """List files deleted in the working tree.
 
436
 
 
437
TODO: Show files deleted since a previous revision, or between two revisions.
 
438
    """
 
439
    b = Branch('.')
 
440
    old = b.basis_tree()
 
441
    new = b.working_tree()
 
442
 
 
443
    ## TODO: Much more efficient way to do this: read in new
 
444
    ## directories with readdir, rather than stating each one.  Same
 
445
    ## level of effort but possibly much less IO.  (Or possibly not,
 
446
    ## if the directories are very large...)
 
447
 
 
448
    for path, ie in old.inventory.iter_entries():
 
449
        if not new.has_id(ie.file_id):
 
450
            if show_ids:
 
451
                print '%-50s %s' % (path, ie.file_id)
 
452
            else:
 
453
                print path
 
454
 
 
455
 
 
456
 
 
457
def cmd_parse_inventory():
 
458
    import cElementTree
 
459
    
 
460
    cElementTree.ElementTree().parse(file('.bzr/inventory'))
 
461
 
 
462
 
 
463
 
 
464
def cmd_load_inventory():
 
465
    """Load inventory for timing purposes"""
 
466
    Branch('.').basis_tree().inventory
 
467
 
 
468
 
 
469
def cmd_dump_inventory():
 
470
    Branch('.').read_working_inventory().write_xml(sys.stdout)
 
471
 
 
472
 
 
473
def cmd_dump_new_inventory():
 
474
    import bzrlib.newinventory
 
475
    inv = Branch('.').basis_tree().inventory
 
476
    bzrlib.newinventory.write_inventory(inv, sys.stdout)
 
477
 
 
478
 
 
479
def cmd_load_new_inventory():
 
480
    import bzrlib.newinventory
 
481
    bzrlib.newinventory.read_new_inventory(sys.stdin)
 
482
                
 
483
    
 
484
def cmd_dump_slacker_inventory():
 
485
    import bzrlib.newinventory
 
486
    inv = Branch('.').basis_tree().inventory
 
487
    bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
 
488
                
 
489
    
 
490
 
342
491
def cmd_root(filename=None):
343
492
    """Print the branch root."""
344
493
    print bzrlib.branch.find_branch_root(filename)
384
533
        print quotefn(f)
385
534
 
386
535
 
 
536
 
 
537
def cmd_ignored():
 
538
    """List ignored files and the patterns that matched them.
 
539
      """
 
540
    tree = Branch('.').working_tree()
 
541
    for path, file_class, kind, file_id in tree.list_files():
 
542
        if file_class != 'I':
 
543
            continue
 
544
        ## XXX: Slightly inefficient since this was already calculated
 
545
        pat = tree.is_ignored(path)
 
546
        print '%-50s %s' % (path, pat)
 
547
 
 
548
 
387
549
def cmd_lookup_revision(revno):
388
550
    try:
389
551
        revno = int(revno)
402
564
    t = b.revision_tree(rh)
403
565
    t.export(dest)
404
566
 
 
567
def cmd_cat(revision, filename):
 
568
    """Print file to stdout."""
 
569
    b = Branch('.')
 
570
    b.print_file(b.relpath(filename), int(revision))
405
571
 
406
572
 
407
573
######################################################################
480
646
 
481
647
 
482
648
def cmd_gen_revision_id():
483
 
    import time
484
649
    print bzrlib.branch._gen_revision_id(time.time())
485
650
 
486
651
 
487
 
def cmd_selftest(verbose=False):
 
652
def cmd_selftest():
488
653
    """Run internal test suite"""
489
654
    ## -v, if present, is seen by doctest; the argument is just here
490
655
    ## so our parser doesn't complain
571
736
    'all':                    None,
572
737
    'help':                   None,
573
738
    'message':                unicode,
 
739
    'profile':                None,
574
740
    'revision':               int,
575
741
    'show-ids':               None,
576
742
    'timezone':               str,
588
754
# listed take none.
589
755
cmd_options = {
590
756
    'add':                    ['verbose'],
 
757
    'cat':                    ['revision'],
591
758
    'commit':                 ['message', 'verbose'],
 
759
    'deleted':                ['show-ids'],
592
760
    'diff':                   ['revision'],
593
761
    'inventory':              ['revision'],
594
 
    'log':                    ['show-ids', 'timezone'],
 
762
    'log':                    ['timezone'],
595
763
    'ls':                     ['revision', 'verbose'],
596
764
    'remove':                 ['verbose'],
597
765
    'status':                 ['all'],
600
768
 
601
769
cmd_args = {
602
770
    'add':                    ['file+'],
 
771
    'cat':                    ['filename'],
603
772
    'commit':                 [],
604
 
    'diff':                   [],
 
773
    'diff':                   ['file*'],
605
774
    'export':                 ['revno', 'dest'],
606
775
    'file-id':                ['filename'],
 
776
    'file-id-path':           ['filename'],
607
777
    'get-file-text':          ['text_id'],
608
778
    'get-inventory':          ['inventory_id'],
609
779
    'get-revision':           ['revision_id'],
612
782
    'init':                   [],
613
783
    'log':                    [],
614
784
    'lookup-revision':        ['revno'],
 
785
    'move':                   ['source$', 'dest'],
615
786
    'relpath':                ['filename'],
616
787
    'remove':                 ['file+'],
 
788
    'rename':                 ['from_name', 'to_name'],
 
789
    'renames':                ['dir?'],
617
790
    'root':                   ['filename?'],
618
791
    'status':                 [],
619
792
    }
671
844
                    else:
672
845
                        optarg = argv.pop(0)
673
846
                opts[optname] = optargfn(optarg)
674
 
                mutter("    option argument %r" % opts[optname])
675
847
            else:
676
848
                if optarg != None:
677
849
                    bailout('option %r takes no argument' % optname)
707
879
        if ap[-1] == '?':
708
880
            if args:
709
881
                argdict[argname] = args.pop(0)
710
 
        elif ap[-1] == '*':
711
 
            assert 0
 
882
        elif ap[-1] == '*': # all remaining arguments
 
883
            if args:
 
884
                argdict[argname + '_list'] = args[:]
 
885
                args = []
 
886
            else:
 
887
                argdict[argname + '_list'] = None
712
888
        elif ap[-1] == '+':
713
889
            if not args:
714
890
                bailout("command %r needs one or more %s"
716
892
            else:
717
893
                argdict[argname + '_list'] = args[:]
718
894
                args = []
 
895
        elif ap[-1] == '$': # all but one
 
896
            if len(args) < 2:
 
897
                bailout("command %r needs one or more %s"
 
898
                        % (cmd, argname.upper()))
 
899
            argdict[argname + '_list'] = args[:-1]
 
900
            args[:-1] = []                
719
901
        else:
720
902
            # just a plain arg
721
903
            argname = ap
744
926
        if 'help' in opts:
745
927
            # TODO: pass down other arguments in case they asked for
746
928
            # help on a command name?
747
 
            cmd_help()
 
929
            if args:
 
930
                cmd_help(args[0])
 
931
            else:
 
932
                cmd_help()
748
933
            return 0
749
934
        elif 'version' in opts:
750
935
            cmd_version()
760
945
    except KeyError:
761
946
        bailout("unknown command " + `cmd`)
762
947
 
763
 
    # TODO: special --profile option to turn on the Python profiler
 
948
    # global option
 
949
    if 'profile' in opts:
 
950
        profile = True
 
951
        del opts['profile']
 
952
    else:
 
953
        profile = False
764
954
 
765
955
    # check options are reasonable
766
956
    allowed = cmd_options.get(cmd, [])
769
959
            bailout("option %r is not allowed for command %r"
770
960
                    % (oname, cmd))
771
961
 
 
962
    # TODO: give an error if there are any mandatory options which are
 
963
    # not specified?  Or maybe there shouldn't be any "mandatory
 
964
    # options" (it is an oxymoron)
 
965
 
 
966
    # mix arguments and options into one dictionary
772
967
    cmdargs = _match_args(cmd, args)
773
 
    cmdargs.update(opts)
774
 
 
775
 
    ret = cmd_handler(**cmdargs) or 0
 
968
    for k, v in opts.items():
 
969
        cmdargs[k.replace('-', '_')] = v
 
970
 
 
971
    if profile:
 
972
        import hotshot
 
973
        prof = hotshot.Profile('.bzr.profile')
 
974
        ret = prof.runcall(cmd_handler, **cmdargs) or 0
 
975
        prof.close()
 
976
 
 
977
        import hotshot.stats
 
978
        stats = hotshot.stats.load('.bzr.profile')
 
979
        #stats.strip_dirs()
 
980
        stats.sort_stats('time')
 
981
        stats.print_stats(20)
 
982
 
 
983
        return ret
 
984
    else:
 
985
        return cmd_handler(**cmdargs) or 0
776
986
 
777
987
 
778
988
 
793
1003
        if len(e.args) > 1:
794
1004
            for h in e.args[1]:
795
1005
                log_error('  ' + h + '\n')
 
1006
        traceback.print_exc(None, bzrlib.trace._tracefile)
 
1007
        log_error('(see $HOME/.bzr.log for debug information)\n')
796
1008
        return 1
797
1009
    except Exception, e:
798
1010
        log_error('bzr: exception: %s\n' % e)
799
 
        log_error('    see .bzr.log for details\n')
 
1011
        log_error('(see $HOME/.bzr.log for debug information)\n')
800
1012
        traceback.print_exc(None, bzrlib.trace._tracefile)
801
 
        traceback.print_exc(None, sys.stderr)
 
1013
        ## traceback.print_exc(None, sys.stderr)
802
1014
        return 1
803
1015
 
804
1016
    # TODO: Maybe nicer handling of IOError?