/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
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
183
1 by mbp at sourcefrog
import from baz patch-364
184
def cmd_inventory(revision=None):
185
    """Show inventory of the current working copy."""
186
    ## TODO: Also optionally show a previous inventory
187
    ## TODO: Format options
188
    b = Branch('.')
189
    if revision == None:
190
        inv = b.read_working_inventory()
191
    else:
192
        inv = b.get_revision_inventory(b.lookup_revision(revision))
193
        
194
    for path, entry in inv.iter_entries():
195
        print '%-50s %s' % (entry.file_id, path)
196
197
198
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
199
def cmd_mv(source_list, dest):
200
    b = Branch('.')
201
202
    b.rename([b.relpath(s) for s in source_list], b.relpath(dest))
203
204
205
168 by mbp at sourcefrog
new "rename" command
206
def cmd_rename(from_name, to_name):
207
    """Change the name of an entry.
208
209
usage: bzr rename FROM_NAME TO_NAME
210
211
examples:
212
  bzr rename frob.c frobber.c
213
  bzr rename src/frob.c lib/frob.c
214
215
It is an error if the destination name exists.
216
217
See also the 'move' command, which moves files into a different
218
directory without changing their name.
219
220
TODO: Some way to rename multiple files without invoking bzr for each
221
one?"""
222
    b = Branch('.')
223
    b.rename_one(b.relpath(from_name), b.relpath(to_name))
224
    
225
226
227
164 by mbp at sourcefrog
new 'renames' command
228
def cmd_renames(dir='.'):
229
    """Show list of renamed files.
230
231
usage: bzr renames [BRANCH]
232
233
TODO: Option to show renames between two historical versions.
234
235
TODO: Only show renames under dir, rather than in the whole branch.
236
"""
237
    b = Branch(dir)
238
    old_inv = b.basis_tree().inventory
239
    new_inv = b.read_working_inventory()
240
    
241
    renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
242
    renames.sort()
243
    for old_name, new_name in renames:
244
        print "%s => %s" % (old_name, new_name)        
245
246
247
1 by mbp at sourcefrog
import from baz patch-364
248
def cmd_info():
112 by mbp at sourcefrog
help for info command
249
    """info: Show statistical information for this branch
250
251
usage: bzr info"""
77 by mbp at sourcefrog
- split info command out into separate file
252
    import info
253
    info.show_info(Branch('.'))        
21 by mbp at sourcefrog
- bzr info: show summary information on branch history
254
    
1 by mbp at sourcefrog
import from baz patch-364
255
256
257
def cmd_remove(file_list, verbose=False):
69 by Martin Pool
handle add, remove, file-id being given filenames that are
258
    b = Branch(file_list[0])
259
    b.remove([b.relpath(f) for f in file_list], verbose=verbose)
1 by mbp at sourcefrog
import from baz patch-364
260
261
262
263
def cmd_file_id(filename):
69 by Martin Pool
handle add, remove, file-id being given filenames that are
264
    b = Branch(filename)
265
    i = b.inventory.path2id(b.relpath(filename))
1 by mbp at sourcefrog
import from baz patch-364
266
    if i is None:
267
        bailout("%s is not a versioned file" % filename)
268
    else:
269
        print i
270
271
272
def cmd_find_filename(fileid):
273
    n = find_filename(fileid)
274
    if n is None:
275
        bailout("%s is not a live file id" % fileid)
276
    else:
277
        print n
278
279
280
def cmd_revision_history():
281
    for patchid in Branch('.').revision_history():
282
        print patchid
283
284
156 by mbp at sourcefrog
new "directories" command
285
def cmd_directories():
286
    for name, ie in Branch('.').read_working_inventory().directories():
287
        if name == '':
288
            print '.'
289
        else:
290
            print name
291
1 by mbp at sourcefrog
import from baz patch-364
292
157 by mbp at sourcefrog
fix test case breakage
293
def cmd_missing():
294
    for name, ie in Branch('.').working_tree().missing():
295
        print name
296
297
1 by mbp at sourcefrog
import from baz patch-364
298
def cmd_init():
299
    # TODO: Check we're not already in a working directory?  At the
300
    # moment you'll get an ugly error.
301
    
302
    # TODO: What if we're in a subdirectory of a branch?  Would like
303
    # to allow that, but then the parent may need to understand that
304
    # the children have disappeared, or should they be versioned in
305
    # both?
306
307
    # TODO: Take an argument/option for branch name.
308
    Branch('.', init=True)
309
310
311
def cmd_diff(revision=None):
109 by mbp at sourcefrog
more help for diff command
312
    """bzr diff: Show differences in working tree.
313
    
314
usage: bzr diff [-r REV]
315
316
--revision REV
317
    Show changes since REV, rather than predecessor.
318
319
TODO: Given two revision arguments, show the difference between them.
320
321
TODO: Allow diff across branches.
322
323
TODO: Option to use external diff command; could be GNU diff, wdiff,
324
or a graphical diff.
325
326
TODO: Diff selected files.
327
"""
328
329
    ## TODO: Shouldn't be in the cmd function.
1 by mbp at sourcefrog
import from baz patch-364
330
331
    b = Branch('.')
332
333
    if revision == None:
334
        old_tree = b.basis_tree()
335
    else:
336
        old_tree = b.revision_tree(b.lookup_revision(revision))
337
        
338
    new_tree = b.working_tree()
339
    old_inv = old_tree.inventory
340
    new_inv = new_tree.inventory
341
342
    # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
343
    old_label = ''
344
    new_label = ''
345
346
    DEVNULL = '/dev/null'
347
    # Windows users, don't panic about this filename -- it is a
348
    # special signal to GNU patch that the file should be created or
349
    # deleted respectively.
350
351
    # TODO: Generation of pseudo-diffs for added/deleted files could
352
    # be usefully made into a much faster special case.
353
354
    # TODO: Better to return them in sorted order I think.
355
    
356
    for file_state, fid, old_name, new_name, kind in bzrlib.diff_trees(old_tree, new_tree):
357
        d = None
358
359
        # Don't show this by default; maybe do it if an option is passed
360
        # idlabel = '      {%s}' % fid
361
        idlabel = ''
362
363
        # FIXME: Something about the diff format makes patch unhappy
364
        # with newly-added files.
365
162 by mbp at sourcefrog
workaround for python2.3 difflib bug
366
        def diffit(oldlines, newlines, **kw):
367
            # FIXME: difflib is wrong if there is no trailing newline.
368
369
            # Special workaround for Python2.3, where difflib fails if
370
            # both sequences are empty.
371
            if oldlines or newlines:
372
                sys.stdout.writelines(difflib.unified_diff(oldlines, newlines, **kw))
1 by mbp at sourcefrog
import from baz patch-364
373
            print
374
        
375
        if file_state in ['.', '?', 'I']:
376
            continue
377
        elif file_state == 'A':
378
            print '*** added %s %r' % (kind, new_name)
379
            if kind == 'file':
380
                diffit([],
381
                       new_tree.get_file(fid).readlines(),
382
                       fromfile=DEVNULL,
383
                       tofile=new_label + new_name + idlabel)
384
        elif file_state == 'D':
385
            assert isinstance(old_name, types.StringTypes)
386
            print '*** deleted %s %r' % (kind, old_name)
387
            if kind == 'file':
388
                diffit(old_tree.get_file(fid).readlines(), [],
389
                       fromfile=old_label + old_name + idlabel,
390
                       tofile=DEVNULL)
391
        elif file_state in ['M', 'R']:
392
            if file_state == 'M':
393
                assert kind == 'file'
394
                assert old_name == new_name
395
                print '*** modified %s %r' % (kind, new_name)
396
            elif file_state == 'R':
397
                print '*** renamed %s %r => %r' % (kind, old_name, new_name)
398
399
            if kind == 'file':
400
                diffit(old_tree.get_file(fid).readlines(),
401
                       new_tree.get_file(fid).readlines(),
402
                       fromfile=old_label + old_name + idlabel,
403
                       tofile=new_label + new_name)
404
        else:
405
            bailout("can't represent state %s {%s}" % (file_state, fid))
406
407
408
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
409
def cmd_deleted(show_ids=False):
135 by mbp at sourcefrog
Simple new 'deleted' command
410
    """List files deleted in the working tree.
411
412
TODO: Show files deleted since a previous revision, or between two revisions.
413
    """
414
    b = Branch('.')
415
    old = b.basis_tree()
416
    new = b.working_tree()
417
147 by mbp at sourcefrog
todo
418
    ## TODO: Much more efficient way to do this: read in new
419
    ## directories with readdir, rather than stating each one.  Same
420
    ## level of effort but possibly much less IO.  (Or possibly not,
421
    ## if the directories are very large...)
422
135 by mbp at sourcefrog
Simple new 'deleted' command
423
    for path, ie in old.inventory.iter_entries():
424
        if not new.has_id(ie.file_id):
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
425
            if show_ids:
426
                print '%-50s %s' % (path, ie.file_id)
427
            else:
428
                print path
148 by mbp at sourcefrog
performance notes and measurements
429
430
431
432
def cmd_parse_inventory():
433
    import cElementTree
434
    
435
    cElementTree.ElementTree().parse(file('.bzr/inventory'))
436
437
438
439
def cmd_load_inventory():
149 by mbp at sourcefrog
experiment with new nested inventory file format
440
    inv = Branch('.').basis_tree().inventory
441
442
443
444
def cmd_dump_new_inventory():
445
    import bzrlib.newinventory
446
    inv = Branch('.').basis_tree().inventory
447
    bzrlib.newinventory.write_inventory(inv, sys.stdout)
151 by mbp at sourcefrog
experimental nested-inventory load support
448
449
450
def cmd_load_new_inventory():
451
    import bzrlib.newinventory
452
    bzrlib.newinventory.read_new_inventory(sys.stdin)
149 by mbp at sourcefrog
experiment with new nested inventory file format
453
                
454
    
455
def cmd_dump_slacker_inventory():
456
    import bzrlib.newinventory
457
    inv = Branch('.').basis_tree().inventory
458
    bzrlib.newinventory.write_slacker_inventory(inv, sys.stdout)
459
                
460
    
135 by mbp at sourcefrog
Simple new 'deleted' command
461
65 by mbp at sourcefrog
rename 'find-branch-root' command to just 'root'
462
def cmd_root(filename=None):
463
    """Print the branch root."""
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
464
    print bzrlib.branch.find_branch_root(filename)
465
    
466
13 by mbp at sourcefrog
fix up cmd_log args
467
def cmd_log(timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
468
    """Show log of this branch.
469
470
    :todo: Options for utc; to show ids; to limit range; etc.
471
    """
12 by mbp at sourcefrog
new --timezone option for bzr log
472
    Branch('.').write_log(show_timezone=timezone)
1 by mbp at sourcefrog
import from baz patch-364
473
474
475
def cmd_ls(revision=None, verbose=False):
476
    """List files in a tree.
477
478
    :todo: Take a revision or remote path and list that tree instead.
479
    """
480
    b = Branch('.')
481
    if revision == None:
482
        tree = b.working_tree()
483
    else:
484
        tree = b.revision_tree(b.lookup_revision(revision))
485
        
486
    for fp, fc, kind, fid in tree.list_files():
487
        if verbose:
488
            if kind == 'directory':
489
                kindch = '/'
490
            elif kind == 'file':
491
                kindch = ''
492
            else:
493
                kindch = '???'
494
                
495
            print '%-8s %s%s' % (fc, fp, kindch)
496
        else:
497
            print fp
498
    
499
    
500
501
def cmd_unknowns():
502
    """List unknown files"""
503
    for f in Branch('.').unknowns():
504
        print quotefn(f)
505
506
133 by mbp at sourcefrog
- new 'ignored' command
507
508
def cmd_ignored(verbose=True):
509
    """List ignored files and the patterns that matched them.
510
      """
511
    tree = Branch('.').working_tree()
512
    for path, file_class, kind, id in tree.list_files():
513
        if file_class != 'I':
514
            continue
515
        ## XXX: Slightly inefficient since this was already calculated
516
        pat = tree.is_ignored(path)
517
        print '%-50s %s' % (path, pat)
518
519
1 by mbp at sourcefrog
import from baz patch-364
520
def cmd_lookup_revision(revno):
521
    try:
522
        revno = int(revno)
523
    except ValueError:
524
        bailout("usage: lookup-revision REVNO",
525
                ["REVNO is a non-negative revision number for this branch"])
526
527
    print Branch('.').lookup_revision(revno) or NONE_STRING
528
529
530
531
def cmd_export(revno, dest):
532
    """Export past revision to destination directory."""
533
    b = Branch('.')
534
    rh = b.lookup_revision(int(revno))
535
    t = b.revision_tree(rh)
536
    t.export(dest)
537
538
539
540
######################################################################
541
# internal/test commands
542
543
544
def cmd_uuid():
545
    """Print a newly-generated UUID."""
63 by mbp at sourcefrog
fix up uuid command
546
    print bzrlib.osutils.uuid()
1 by mbp at sourcefrog
import from baz patch-364
547
548
549
8 by mbp at sourcefrog
store committer's timezone in revision and show
550
def cmd_local_time_offset():
551
    print bzrlib.osutils.local_time_offset()
552
553
554
57 by mbp at sourcefrog
error if --message is not given for commit
555
def cmd_commit(message=None, verbose=False):
97 by mbp at sourcefrog
- more commit help
556
    """Commit changes to a new revision.
557
558
--message MESSAGE
559
    Description of changes in this revision; free form text.
560
    It is recommended that the first line be a single-sentence
561
    summary.
562
--verbose
563
    Show status of changed files,
564
565
TODO: Commit only selected files.
566
567
TODO: Run hooks on tree to-be-committed, and after commit.
568
569
TODO: Strict commit that fails if there are unknown or deleted files.
570
"""
571
57 by mbp at sourcefrog
error if --message is not given for commit
572
    if not message:
573
        bailout("please specify a commit message")
1 by mbp at sourcefrog
import from baz patch-364
574
    Branch('.').commit(message, verbose=verbose)
575
576
113 by mbp at sourcefrog
More help for check command
577
def cmd_check(dir='.'):
578
    """check: Consistency check of branch history.
579
580
usage: bzr check [-v] [BRANCH]
581
582
options:
583
  --verbose, -v         Show progress of checking.
584
585
This command checks various invariants about the branch storage to
586
detect data corruption or bzr bugs.
587
"""
588
    import bzrlib.check
589
    bzrlib.check.check(Branch(dir, find_root=False))
1 by mbp at sourcefrog
import from baz patch-364
590
591
592
def cmd_is(pred, *rest):
593
    """Test whether PREDICATE is true."""
594
    try:
595
        cmd_handler = globals()['assert_' + pred.replace('-', '_')]
596
    except KeyError:
597
        bailout("unknown predicate: %s" % quotefn(pred))
598
        
599
    try:
600
        cmd_handler(*rest)
601
    except BzrCheckError:
602
        # by default we don't print the message so that this can
603
        # be used from shell scripts without producing noise
604
        sys.exit(1)
605
606
607
def cmd_username():
608
    print bzrlib.osutils.username()
609
610
611
def cmd_user_email():
612
    print bzrlib.osutils.user_email()
613
614
615
def cmd_gen_revision_id():
616
    import time
617
    print bzrlib.branch._gen_revision_id(time.time())
618
619
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
620
def cmd_selftest(verbose=False):
621
    """Run internal test suite"""
1 by mbp at sourcefrog
import from baz patch-364
622
    ## -v, if present, is seen by doctest; the argument is just here
623
    ## so our parser doesn't complain
624
625
    ## TODO: --verbose option
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
626
627
    failures, tests = 0, 0
1 by mbp at sourcefrog
import from baz patch-364
628
    
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
629
    import doctest, bzrlib.store, bzrlib.tests
1 by mbp at sourcefrog
import from baz patch-364
630
    bzrlib.trace.verbose = False
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
631
632
    for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
70 by mbp at sourcefrog
Prepare for smart recursive add.
633
        bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
55 by mbp at sourcefrog
bzr selftest shows some counts of tests
634
        mf, mt = doctest.testmod(m)
635
        failures += mf
636
        tests += mt
637
        print '%-40s %3d tests' % (m.__name__, mt),
638
        if mf:
639
            print '%3d FAILED!' % mf
640
        else:
641
            print
642
643
    print '%-40s %3d tests' % ('total', tests),
644
    if failures:
645
        print '%3d FAILED!' % failures
646
    else:
647
        print
648
649
650
651
# deprecated
652
cmd_doctest = cmd_selftest
53 by mbp at sourcefrog
'selftest' command instead of 'doctest'
653
654
1 by mbp at sourcefrog
import from baz patch-364
655
######################################################################
656
# help
657
658
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
659
def cmd_help(topic=None):
660
    if topic == None:
661
        print __doc__
662
        return
663
664
    # otherwise, maybe the name of a command?
665
    try:
666
        cmdfn = globals()['cmd_' + topic.replace('-', '_')]
667
    except KeyError:
668
        bailout("no help for %r" % topic)
669
670
    doc = cmdfn.__doc__
671
    if doc == None:
672
        bailout("sorry, no detailed help yet for %r" % topic)
673
674
    print doc
675
        
676
1 by mbp at sourcefrog
import from baz patch-364
677
678
679
def cmd_version():
84 by mbp at sourcefrog
- update version string
680
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
681
    print bzrlib.__copyright__
1 by mbp at sourcefrog
import from baz patch-364
682
    print "http://bazaar-ng.org/"
683
    print
684
    print \
685
"""bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and
686
you may use, modify and redistribute it under the terms of the GNU 
687
General Public License version 2 or later."""
688
689
690
def cmd_rocks():
691
    """Statement of optimism."""
692
    print "it sure does!"
693
694
695
696
######################################################################
697
# main routine
698
699
700
# list of all available options; the rhs can be either None for an
701
# option that takes no argument, or a constructor function that checks
702
# the type.
703
OPTIONS = {
704
    'all':                    None,
705
    'help':                   None,
706
    'message':                unicode,
137 by mbp at sourcefrog
new --profile option
707
    'profile':                None,
1 by mbp at sourcefrog
import from baz patch-364
708
    'revision':               int,
709
    'show-ids':               None,
12 by mbp at sourcefrog
new --timezone option for bzr log
710
    'timezone':               str,
1 by mbp at sourcefrog
import from baz patch-364
711
    'verbose':                None,
712
    'version':                None,
713
    }
714
715
SHORT_OPTIONS = {
716
    'm':                      'message',
717
    'r':                      'revision',
718
    'v':                      'verbose',
719
}
720
721
# List of options that apply to particular commands; commands not
722
# listed take none.
723
cmd_options = {
724
    'add':                    ['verbose'],
725
    'commit':                 ['message', 'verbose'],
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
726
    'deleted':                ['show-ids'],
1 by mbp at sourcefrog
import from baz patch-364
727
    'diff':                   ['revision'],
728
    'inventory':              ['revision'],
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
729
    'log':                    ['timezone'],
1 by mbp at sourcefrog
import from baz patch-364
730
    'ls':                     ['revision', 'verbose'],
12 by mbp at sourcefrog
new --timezone option for bzr log
731
    'remove':                 ['verbose'],
1 by mbp at sourcefrog
import from baz patch-364
732
    'status':                 ['all'],
733
    }
734
735
736
cmd_args = {
737
    'add':                    ['file+'],
738
    'commit':                 [],
739
    'diff':                   [],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
740
    'export':                 ['revno', 'dest'],
1 by mbp at sourcefrog
import from baz patch-364
741
    'file-id':                ['filename'],
742
    'get-file-text':          ['text_id'],
743
    'get-inventory':          ['inventory_id'],
744
    'get-revision':           ['revision_id'],
745
    'get-revision-inventory': ['revision_id'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
746
    'help':                   ['topic?'],
747
    'init':                   [],
1 by mbp at sourcefrog
import from baz patch-364
748
    'log':                    [],
749
    'lookup-revision':        ['revno'],
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
750
    'mv':                     ['source$', 'dest'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
751
    'relpath':                ['filename'],
1 by mbp at sourcefrog
import from baz patch-364
752
    'remove':                 ['file+'],
168 by mbp at sourcefrog
new "rename" command
753
    'rename':                 ['from_name', 'to_name'],
164 by mbp at sourcefrog
new 'renames' command
754
    'renames':                ['dir?'],
83 by mbp at sourcefrog
Can now say "bzr help COMMAND" for more detailed help
755
    'root':                   ['filename?'],
1 by mbp at sourcefrog
import from baz patch-364
756
    'status':                 [],
757
    }
758
759
760
def parse_args(argv):
761
    """Parse command line.
762
    
763
    Arguments and options are parsed at this level before being passed
764
    down to specific command handlers.  This routine knows, from a
765
    lookup table, something about the available options, what optargs
766
    they take, and which commands will accept them.
767
31 by Martin Pool
fix up parse_args doctest
768
    >>> parse_args('--help'.split())
1 by mbp at sourcefrog
import from baz patch-364
769
    ([], {'help': True})
31 by Martin Pool
fix up parse_args doctest
770
    >>> parse_args('--version'.split())
1 by mbp at sourcefrog
import from baz patch-364
771
    ([], {'version': True})
31 by Martin Pool
fix up parse_args doctest
772
    >>> parse_args('status --all'.split())
1 by mbp at sourcefrog
import from baz patch-364
773
    (['status'], {'all': True})
31 by Martin Pool
fix up parse_args doctest
774
    >>> parse_args('commit --message=biter'.split())
17 by mbp at sourcefrog
allow --option=ARG syntax
775
    (['commit'], {'message': u'biter'})
1 by mbp at sourcefrog
import from baz patch-364
776
    """
777
    args = []
778
    opts = {}
779
780
    # TODO: Maybe handle '--' to end options?
781
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
782
    while argv:
783
        a = argv.pop(0)
1 by mbp at sourcefrog
import from baz patch-364
784
        if a[0] == '-':
17 by mbp at sourcefrog
allow --option=ARG syntax
785
            optarg = None
1 by mbp at sourcefrog
import from baz patch-364
786
            if a[1] == '-':
787
                mutter("  got option %r" % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
788
                if '=' in a:
789
                    optname, optarg = a[2:].split('=', 1)
790
                else:
791
                    optname = a[2:]
1 by mbp at sourcefrog
import from baz patch-364
792
                if optname not in OPTIONS:
793
                    bailout('unknown long option %r' % a)
794
            else:
795
                shortopt = a[1:]
796
                if shortopt not in SHORT_OPTIONS:
797
                    bailout('unknown short option %r' % a)
798
                optname = SHORT_OPTIONS[shortopt]
799
            
800
            if optname in opts:
801
                # XXX: Do we ever want to support this, e.g. for -r?
802
                bailout('repeated option %r' % a)
17 by mbp at sourcefrog
allow --option=ARG syntax
803
                
1 by mbp at sourcefrog
import from baz patch-364
804
            optargfn = OPTIONS[optname]
805
            if optargfn:
17 by mbp at sourcefrog
allow --option=ARG syntax
806
                if optarg == None:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
807
                    if not argv:
17 by mbp at sourcefrog
allow --option=ARG syntax
808
                        bailout('option %r needs an argument' % a)
809
                    else:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
810
                        optarg = argv.pop(0)
17 by mbp at sourcefrog
allow --option=ARG syntax
811
                opts[optname] = optargfn(optarg)
1 by mbp at sourcefrog
import from baz patch-364
812
            else:
17 by mbp at sourcefrog
allow --option=ARG syntax
813
                if optarg != None:
814
                    bailout('option %r takes no argument' % optname)
1 by mbp at sourcefrog
import from baz patch-364
815
                opts[optname] = True
816
        else:
817
            args.append(a)
818
819
    return args, opts
820
821
822
823
def _match_args(cmd, args):
824
    """Check non-option arguments match required pattern.
825
826
    >>> _match_args('status', ['asdasdsadasd'])
827
    Traceback (most recent call last):
828
    ...
829
    BzrError: ("extra arguments to command status: ['asdasdsadasd']", [])
830
    >>> _match_args('add', ['asdasdsadasd'])
831
    {'file_list': ['asdasdsadasd']}
832
    >>> _match_args('add', 'abc def gj'.split())
833
    {'file_list': ['abc', 'def', 'gj']}
834
    """
835
    # match argument pattern
836
    argform = cmd_args.get(cmd, [])
837
    argdict = {}
838
    # TODO: Need a way to express 'cp SRC... DEST', where it matches
839
    # all but one.
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
840
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
841
    # step through args and argform, allowing appropriate 0-many matches
1 by mbp at sourcefrog
import from baz patch-364
842
    for ap in argform:
843
        argname = ap[:-1]
844
        if ap[-1] == '?':
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
845
            if args:
846
                argdict[argname] = args.pop(0)
1 by mbp at sourcefrog
import from baz patch-364
847
        elif ap[-1] == '*':
848
            assert 0
849
        elif ap[-1] == '+':
850
            if not args:
851
                bailout("command %r needs one or more %s"
852
                        % (cmd, argname.upper()))
853
            else:
854
                argdict[argname + '_list'] = args[:]
855
                args = []
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
856
        elif ap[-1] == '$': # all but one
857
            if len(args) < 2:
858
                bailout("command %r needs one or more %s"
859
                        % (cmd, argname.upper()))
860
            argdict[argname + '_list'] = args[:-1]
861
            args[:-1] = []                
1 by mbp at sourcefrog
import from baz patch-364
862
        else:
863
            # just a plain arg
864
            argname = ap
865
            if not args:
866
                bailout("command %r requires argument %s"
867
                        % (cmd, argname.upper()))
868
            else:
869
                argdict[argname] = args.pop(0)
870
            
871
    if args:
872
        bailout("extra arguments to command %s: %r"
873
                % (cmd, args))
874
875
    return argdict
876
877
878
879
def run_bzr(argv):
880
    """Execute a command.
881
882
    This is similar to main(), but without all the trappings for
883
    logging and error handling.
884
    """
885
    try:
26 by mbp at sourcefrog
fix StopIteration error on python2.3(?)
886
        args, opts = parse_args(argv[1:])
1 by mbp at sourcefrog
import from baz patch-364
887
        if 'help' in opts:
888
            # TODO: pass down other arguments in case they asked for
889
            # help on a command name?
159 by mbp at sourcefrog
bzr commit --help now works
890
            if args:
891
                cmd_help(args[0])
892
            else:
893
                cmd_help()
1 by mbp at sourcefrog
import from baz patch-364
894
            return 0
895
        elif 'version' in opts:
896
            cmd_version()
897
            return 0
898
        cmd = args.pop(0)
899
    except IndexError:
900
        log_error('usage: bzr COMMAND\n')
901
        log_error('  try "bzr help"\n')
902
        return 1
115 by mbp at sourcefrog
todo
903
1 by mbp at sourcefrog
import from baz patch-364
904
    try:
905
        cmd_handler = globals()['cmd_' + cmd.replace('-', '_')]
906
    except KeyError:
907
        bailout("unknown command " + `cmd`)
908
137 by mbp at sourcefrog
new --profile option
909
    # global option
910
    if 'profile' in opts:
911
        profile = True
912
        del opts['profile']
913
    else:
914
        profile = False
1 by mbp at sourcefrog
import from baz patch-364
915
916
    # check options are reasonable
917
    allowed = cmd_options.get(cmd, [])
918
    for oname in opts:
919
        if oname not in allowed:
920
            bailout("option %r is not allowed for command %r"
921
                    % (oname, cmd))
922
137 by mbp at sourcefrog
new --profile option
923
    # mix arguments and options into one dictionary
1 by mbp at sourcefrog
import from baz patch-364
924
    cmdargs = _match_args(cmd, args)
136 by mbp at sourcefrog
new --show-ids option for 'deleted' command
925
    for k, v in opts.items():
926
        cmdargs[k.replace('-', '_')] = v
1 by mbp at sourcefrog
import from baz patch-364
927
137 by mbp at sourcefrog
new --profile option
928
    if profile:
929
        import hotshot
930
        prof = hotshot.Profile('.bzr.profile')
931
        ret = prof.runcall(cmd_handler, **cmdargs) or 0
932
        prof.close()
933
934
        import hotshot.stats
935
        stats = hotshot.stats.load('.bzr.profile')
936
        #stats.strip_dirs()
149 by mbp at sourcefrog
experiment with new nested inventory file format
937
        stats.sort_stats('time')
137 by mbp at sourcefrog
new --profile option
938
        stats.print_stats(20)
939
    else:
940
        return cmd_handler(**cmdargs) or 0
1 by mbp at sourcefrog
import from baz patch-364
941
942
943
944
def main(argv):
945
    ## TODO: Handle command-line options; probably know what options are valid for
946
    ## each command
947
948
    ## TODO: If the arguments are wrong, give a usage message rather
949
    ## than just a backtrace.
950
59 by mbp at sourcefrog
lift out tracefile creation code
951
    bzrlib.trace.create_tracefile(argv)
952
    
1 by mbp at sourcefrog
import from baz patch-364
953
    try:
954
        ret = run_bzr(argv)
955
        return ret
956
    except BzrError, e:
957
        log_error('bzr: error: ' + e.args[0] + '\n')
958
        if len(e.args) > 1:
959
            for h in e.args[1]:
960
                log_error('  ' + h + '\n')
961
        return 1
962
    except Exception, e:
963
        log_error('bzr: exception: %s\n' % e)
964
        log_error('    see .bzr.log for details\n')
965
        traceback.print_exc(None, bzrlib.trace._tracefile)
966
        traceback.print_exc(None, sys.stderr)
967
        return 1
968
969
    # TODO: Maybe nicer handling of IOError?
970
971
972
973
if __name__ == '__main__':
974
    sys.exit(main(sys.argv))
975
    ##import profile
976
    ##profile.run('main(sys.argv)')
977