~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-05-03 08:00:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050503080027-908edb5b39982198
doc

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
 
18
 
 
19
 
import sys, os, time, os.path
 
17
"""Bazaar-NG -- a free distributed version-control tool
 
18
http://bazaar-ng.org/
 
19
 
 
20
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
 
21
 
 
22
* Metadata format is not stable yet -- you may need to
 
23
  discard history in the future.
 
24
 
 
25
* Many commands unimplemented or partially implemented.
 
26
 
 
27
* Space-inefficient storage.
 
28
 
 
29
* No merge operators yet.
 
30
 
 
31
Interesting commands:
 
32
 
 
33
  bzr help [COMMAND]
 
34
      Show help screen
 
35
  bzr version
 
36
      Show software version/licence/non-warranty.
 
37
  bzr init
 
38
      Start versioning the current directory
 
39
  bzr add FILE...
 
40
      Make files versioned.
 
41
  bzr log
 
42
      Show revision history.
 
43
  bzr rename FROM TO
 
44
      Rename one file.
 
45
  bzr move FROM... DESTDIR
 
46
      Move one or more files to a different directory.
 
47
  bzr diff [FILE...]
 
48
      Show changes from last revision to working copy.
 
49
  bzr commit -m 'MESSAGE'
 
50
      Store current state as new revision.
 
51
  bzr export [-r REVNO] DESTINATION
 
52
      Export the branch state at a previous version.
 
53
  bzr status
 
54
      Show summary of pending changes.
 
55
  bzr remove FILE...
 
56
      Make a file not versioned.
 
57
  bzr info
 
58
      Show statistics about this branch.
 
59
  bzr check
 
60
      Verify history is stored safely. 
 
61
  (for more type 'bzr help commands')
 
62
"""
 
63
 
 
64
 
 
65
 
 
66
 
 
67
import sys, os, time, types, shutil, tempfile, fnmatch, difflib, os.path
20
68
from sets import Set
 
69
from pprint import pprint
 
70
from stat import *
 
71
from glob import glob
21
72
 
22
73
import bzrlib
 
74
from bzrlib.store import ImmutableStore
23
75
from bzrlib.trace import mutter, note, log_error
24
76
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
25
77
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
26
 
from bzrlib.tree import RevisionTree, EmptyTree, Tree
 
78
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
27
79
from bzrlib.revision import Revision
28
80
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
29
81
     format_date
30
 
from bzrlib import merge
31
 
 
32
 
 
33
 
def _squish_command_name(cmd):
34
 
    return 'cmd_' + cmd.replace('-', '_')
35
 
 
36
 
 
37
 
def _unsquish_command_name(cmd):
38
 
    assert cmd.startswith("cmd_")
39
 
    return cmd[4:].replace('_','-')
40
 
 
41
 
def get_all_cmds():
42
 
    """Return canonical name and class for all registered commands."""
43
 
    for k, v in globals().iteritems():
44
 
        if k.startswith("cmd_"):
45
 
            yield _unsquish_command_name(k), v
 
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
CMD_ALIASES = {
 
92
    '?':         'help',
 
93
    'ci':        'commit',
 
94
    'checkin':   'commit',
 
95
    'di':        'diff',
 
96
    'st':        'status',
 
97
    'stat':      'status',
 
98
    }
 
99
 
46
100
 
47
101
def get_cmd_class(cmd):
48
 
    """Return the canonical name and command class for a command.
49
 
    """
50
 
    cmd = str(cmd)                      # not unicode
51
 
 
52
 
    # first look up this command under the specified name
 
102
    cmd = str(cmd)
 
103
    
 
104
    cmd = CMD_ALIASES.get(cmd, cmd)
 
105
    
53
106
    try:
54
 
        return cmd, globals()[_squish_command_name(cmd)]
 
107
        cmd_class = globals()['cmd_' + cmd.replace('-', '_')]
55
108
    except KeyError:
56
 
        pass
57
 
 
58
 
    # look for any command which claims this as an alias
59
 
    for cmdname, cmdclass in get_all_cmds():
60
 
        if cmd in cmdclass.aliases:
61
 
            return cmdname, cmdclass
62
 
 
63
 
    cmdclass = ExternalCommand.find_command(cmd)
64
 
    if cmdclass:
65
 
        return cmd, cmdclass
66
 
 
67
 
    raise BzrCommandError("unknown command %r" % cmd)
 
109
        raise BzrError("unknown command %r" % cmd)
 
110
 
 
111
    return cmd, cmd_class
 
112
 
68
113
 
69
114
 
70
115
class Command:
110
155
        This is invoked with the options and arguments bound to
111
156
        keyword parameters.
112
157
 
113
 
        Return 0 or None if the command was successful, or a shell
114
 
        error code if not.
 
158
        Return True if the command was successful, False if not.
115
159
        """
116
 
        return 0
117
 
 
118
 
 
119
 
class ExternalCommand(Command):
120
 
    """Class to wrap external commands.
121
 
 
122
 
    We cheat a little here, when get_cmd_class() calls us we actually give it back
123
 
    an object we construct that has the appropriate path, help, options etc for the
124
 
    specified command.
125
 
 
126
 
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
127
 
    method, which we override to call the Command.__init__ method. That then calls
128
 
    our run method which is pretty straight forward.
129
 
 
130
 
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
131
 
    back into command line options and arguments for the script.
132
 
    """
133
 
 
134
 
    def find_command(cls, cmd):
135
 
        bzrpath = os.environ.get('BZRPATH', '')
136
 
 
137
 
        for dir in bzrpath.split(':'):
138
 
            path = os.path.join(dir, cmd)
139
 
            if os.path.isfile(path):
140
 
                return ExternalCommand(path)
141
 
 
142
 
        return None
143
 
 
144
 
    find_command = classmethod(find_command)
145
 
 
146
 
    def __init__(self, path):
147
 
        self.path = path
148
 
 
149
 
        # TODO: If either of these fail, we should detect that and
150
 
        # assume that path is not really a bzr plugin after all.
151
 
 
152
 
        pipe = os.popen('%s --bzr-usage' % path, 'r')
153
 
        self.takes_options = pipe.readline().split()
154
 
        self.takes_args = pipe.readline().split()
155
 
        pipe.close()
156
 
 
157
 
        pipe = os.popen('%s --bzr-help' % path, 'r')
158
 
        self.__doc__ = pipe.read()
159
 
        pipe.close()
160
 
 
161
 
    def __call__(self, options, arguments):
162
 
        Command.__init__(self, options, arguments)
163
 
        return self
164
 
 
165
 
    def run(self, **kargs):
166
 
        opts = []
167
 
        args = []
168
 
 
169
 
        keys = kargs.keys()
170
 
        keys.sort()
171
 
        for name in keys:
172
 
            value = kargs[name]
173
 
            if OPTIONS.has_key(name):
174
 
                # it's an option
175
 
                opts.append('--%s' % name)
176
 
                if value is not None and value is not True:
177
 
                    opts.append(str(value))
178
 
            else:
179
 
                # it's an arg, or arg list
180
 
                if type(value) is not list:
181
 
                    value = [value]
182
 
                for v in value:
183
 
                    if v is not None:
184
 
                        args.append(str(v))
185
 
 
186
 
        self.status = os.spawnv(os.P_WAIT, self.path, [self.path] + opts + args)
187
 
        return self.status
 
160
        return True
 
161
 
188
162
 
189
163
 
190
164
class cmd_status(Command):
191
165
    """Display status summary.
192
166
 
193
 
    This reports on versioned and unknown files, reporting them
194
 
    grouped by state.  Possible states are:
195
 
 
196
 
    added
197
 
        Versioned in the working copy but not in the previous revision.
198
 
 
199
 
    removed
200
 
        Versioned in the previous revision but removed or deleted
201
 
        in the working copy.
202
 
 
203
 
    renamed
204
 
        Path of this file changed from the previous revision;
205
 
        the text may also have changed.  This includes files whose
206
 
        parent directory was renamed.
207
 
 
208
 
    modified
209
 
        Text has changed since the previous revision.
210
 
 
211
 
    unchanged
212
 
        Nothing about this file has changed since the previous revision.
213
 
        Only shown with --all.
214
 
 
215
 
    unknown
216
 
        Not versioned and not matching an ignore pattern.
217
 
 
218
 
    To see ignored files use 'bzr ignored'.  For details in the
219
 
    changes to file texts, use 'bzr diff'.
220
 
 
221
 
    If no arguments are specified, the status of the entire working
222
 
    directory is shown.  Otherwise, only the status of the specified
223
 
    files or directories is reported.  If a directory is given, status
224
 
    is reported for everything inside that directory.
 
167
    For each file there is a single line giving its file state and name.
 
168
    The name is that in the current revision unless it is deleted or
 
169
    missing, in which case the old name is shown.
225
170
    """
226
 
    takes_args = ['file*']
227
 
    takes_options = ['all', 'show-ids']
228
 
    aliases = ['st', 'stat']
 
171
    takes_options = ['all']
229
172
    
230
 
    def run(self, all=False, show_ids=False, file_list=None):
231
 
        if file_list:
232
 
            b = Branch(file_list[0], lock_mode='r')
233
 
            file_list = [b.relpath(x) for x in file_list]
234
 
            # special case: only one path was given and it's the root
235
 
            # of the branch
236
 
            if file_list == ['']:
237
 
                file_list = None
238
 
        else:
239
 
            b = Branch('.', lock_mode='r')
240
 
        import status
241
 
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
242
 
                           specific_files=file_list)
 
173
    def run(self, all=False):
 
174
        #import bzrlib.status
 
175
        #bzrlib.status.tree_status(Branch('.'))
 
176
        Branch('.').show_status(show_all=all)
243
177
 
244
178
 
245
179
class cmd_cat_revision(Command):
288
222
        bzrlib.add.smart_add(file_list, verbose)
289
223
 
290
224
 
291
 
class cmd_relpath(Command):
 
225
def Relpath(Command):
292
226
    """Show path of a file relative to root"""
293
 
    takes_args = ['filename']
 
227
    takes_args = ('filename')
294
228
    
295
 
    def run(self, filename):
296
 
        print Branch(filename).relpath(filename)
 
229
    def run(self):
 
230
        print Branch(self.args['filename']).relpath(filename)
297
231
 
298
232
 
299
233
 
370
304
 
371
305
 
372
306
class cmd_info(Command):
373
 
    """Show statistical information about a branch."""
374
 
    takes_args = ['branch?']
375
 
    
376
 
    def run(self, branch=None):
 
307
    """Show statistical information for this branch"""
 
308
    def run(self):
377
309
        import info
378
 
 
379
 
        from branch import find_branch
380
 
        b = find_branch(branch)
381
 
        info.show_info(b)
 
310
        info.show_info(Branch('.'))        
382
311
 
383
312
 
384
313
class cmd_remove(Command):
490
419
    
491
420
    takes_args = ['file*']
492
421
    takes_options = ['revision']
493
 
    aliases = ['di']
494
422
 
495
423
    def run(self, revision=None, file_list=None):
496
424
        from bzrlib.diff import show_diff
497
425
    
498
 
        show_diff(Branch('.'), revision, specific_files=file_list)
499
 
 
500
 
 
501
 
        
 
426
        show_diff(Branch('.'), revision, file_list)
502
427
 
503
428
 
504
429
class cmd_deleted(Command):
523
448
                else:
524
449
                    print path
525
450
 
526
 
 
527
 
class cmd_modified(Command):
528
 
    """List files modified in working tree."""
529
 
    hidden = True
530
 
    def run(self):
531
 
        import statcache
532
 
        b = Branch('.')
533
 
        inv = b.read_working_inventory()
534
 
        sc = statcache.update_cache(b, inv)
535
 
        basis = b.basis_tree()
536
 
        basis_inv = basis.inventory
537
 
        
538
 
        # We used to do this through iter_entries(), but that's slow
539
 
        # when most of the files are unmodified, as is usually the
540
 
        # case.  So instead we iterate by inventory entry, and only
541
 
        # calculate paths as necessary.
542
 
 
543
 
        for file_id in basis_inv:
544
 
            cacheentry = sc.get(file_id)
545
 
            if not cacheentry:                 # deleted
546
 
                continue
547
 
            ie = basis_inv[file_id]
548
 
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
549
 
                path = inv.id2path(file_id)
550
 
                print path
551
 
 
552
 
 
553
 
 
554
 
class cmd_added(Command):
555
 
    """List files added in working tree."""
556
 
    hidden = True
557
 
    def run(self):
558
 
        b = Branch('.')
559
 
        wt = b.working_tree()
560
 
        basis_inv = b.basis_tree().inventory
561
 
        inv = wt.inventory
562
 
        for file_id in inv:
563
 
            if file_id in basis_inv:
564
 
                continue
565
 
            path = inv.id2path(file_id)
566
 
            if not os.access(b.abspath(path), os.F_OK):
567
 
                continue
568
 
            print path
569
 
                
570
 
        
571
 
 
572
451
class cmd_root(Command):
573
452
    """Show the tree root directory.
574
453
 
577
456
    takes_args = ['filename?']
578
457
    def run(self, filename=None):
579
458
        """Print the branch root."""
580
 
        from branch import find_branch
581
 
        b = find_branch(filename)
582
 
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
 
459
        print bzrlib.branch.find_branch_root(filename)
 
460
 
583
461
 
584
462
 
585
463
class cmd_log(Command):
586
464
    """Show log of this branch.
587
465
 
588
 
    TODO: Option to limit range.
589
 
 
590
 
    TODO: Perhaps show most-recent first with an option for last.
 
466
    TODO: Options to show ids; to limit range; etc.
591
467
    """
592
 
    takes_args = ['filename?']
593
 
    takes_options = ['timezone', 'verbose', 'show-ids']
594
 
    def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
595
 
        from branch import find_branch
596
 
        b = find_branch((filename or '.'), lock_mode='r')
597
 
        if filename:
598
 
            filename = b.relpath(filename)
599
 
        bzrlib.show_log(b, filename,
600
 
                        show_timezone=timezone,
601
 
                        verbose=verbose,
602
 
                        show_ids=show_ids)
603
 
 
604
 
 
605
 
 
606
 
class cmd_touching_revisions(Command):
607
 
    """Return revision-ids which affected a particular file."""
608
 
    hidden = True
609
 
    takes_args = ["filename"]
610
 
    def run(self, filename):
611
 
        b = Branch(filename, lock_mode='r')
612
 
        inv = b.read_working_inventory()
613
 
        file_id = inv.path2id(b.relpath(filename))
614
 
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
615
 
            print "%6d %s" % (revno, what)
 
468
    takes_options = ['timezone', 'verbose']
 
469
    def run(self, timezone='original', verbose=False):
 
470
        Branch('.').write_log(show_timezone=timezone, verbose=verbose)
616
471
 
617
472
 
618
473
class cmd_ls(Command):
652
507
 
653
508
 
654
509
class cmd_ignore(Command):
655
 
    """Ignore a command or pattern
656
 
 
657
 
    To remove patterns from the ignore list, edit the .bzrignore file.
658
 
 
659
 
    If the pattern contains a slash, it is compared to the whole path
660
 
    from the branch root.  Otherwise, it is comapred to only the last
661
 
    component of the path.
662
 
 
663
 
    Ignore patterns are case-insensitive on case-insensitive systems.
664
 
 
665
 
    Note: wildcards must be quoted from the shell on Unix.
666
 
 
667
 
    examples:
668
 
        bzr ignore ./Makefile
669
 
        bzr ignore '*.class'
670
 
    """
 
510
    """Ignore a command or pattern"""
671
511
    takes_args = ['name_pattern']
672
512
    
673
513
    def run(self, name_pattern):
674
 
        from bzrlib.atomicfile import AtomicFile
675
 
        import codecs
676
 
 
677
514
        b = Branch('.')
678
 
        ifn = b.abspath('.bzrignore')
679
 
 
680
 
        # FIXME: probably doesn't handle non-ascii patterns
681
 
 
682
 
        if os.path.exists(ifn):
683
 
            f = b.controlfile(ifn, 'rt')
684
 
            igns = f.read()
685
 
            f.close()
686
 
        else:
687
 
            igns = ''
688
 
 
689
 
        if igns and igns[-1] != '\n':
690
 
            igns += '\n'
691
 
        igns += name_pattern + '\n'
692
 
 
693
 
        f = AtomicFile(ifn, 'wt')
694
 
        f.write(igns)
695
 
        f.commit()
 
515
 
 
516
        # XXX: This will fail if it's a hardlink; should use an AtomicFile class.
 
517
        f = open(b.abspath('.bzrignore'), 'at')
 
518
        f.write(name_pattern + '\n')
 
519
        f.close()
696
520
 
697
521
        inv = b.working_tree().inventory
698
522
        if inv.path2id('.bzrignore'):
704
528
 
705
529
 
706
530
class cmd_ignored(Command):
707
 
    """List ignored files and the patterns that matched them.
708
 
 
709
 
    See also: bzr ignore"""
 
531
    """List ignored files and the patterns that matched them."""
710
532
    def run(self):
711
533
        tree = Branch('.').working_tree()
712
534
        for path, file_class, kind, file_id in tree.list_files():
722
544
 
723
545
    example:
724
546
        bzr lookup-revision 33
725
 
    """
 
547
        """
726
548
    hidden = True
727
 
    takes_args = ['revno']
728
 
    
729
549
    def run(self, revno):
730
550
        try:
731
551
            revno = int(revno)
732
552
        except ValueError:
733
 
            raise BzrCommandError("not a valid revision-number: %r" % revno)
734
 
 
735
 
        print Branch('.').lookup_revision(revno)
 
553
            raise BzrError("not a valid revision-number: %r" % revno)
 
554
 
 
555
        print Branch('.').lookup_revision(revno) or NONE_STRING
 
556
 
736
557
 
737
558
 
738
559
class cmd_export(Command):
741
562
    If no revision is specified this exports the last committed revision."""
742
563
    takes_args = ['dest']
743
564
    takes_options = ['revision']
744
 
    def run(self, dest, revision=None):
 
565
    def run(self, dest, revno=None):
745
566
        b = Branch('.')
746
 
        if revision == None:
747
 
            rh = b.revision_history()[-1]
 
567
        if revno == None:
 
568
            rh = b.revision_history[-1]
748
569
        else:
749
 
            rh = b.lookup_revision(int(revision))
 
570
            rh = b.lookup_revision(int(revno))
750
571
        t = b.revision_tree(rh)
751
572
        t.export(dest)
752
573
 
775
596
class cmd_commit(Command):
776
597
    """Commit changes into a new revision.
777
598
 
778
 
    If selected files are specified, only changes to those files are
779
 
    committed.  If a directory is specified then its contents are also
780
 
    committed.
781
 
 
782
 
    A selected-file commit may fail in some cases where the committed
783
 
    tree would be invalid, such as trying to commit a file in a
784
 
    newly-added directory that is not itself committed.
 
599
    TODO: Commit only selected files.
785
600
 
786
601
    TODO: Run hooks on tree to-be-committed, and after commit.
787
602
 
788
603
    TODO: Strict commit that fails if there are unknown or deleted files.
789
604
    """
790
 
    takes_args = ['selected*']
791
 
    takes_options = ['message', 'file', 'verbose']
792
 
    aliases = ['ci', 'checkin']
793
 
 
794
 
    def run(self, message=None, file=None, verbose=False, selected_list=None):
795
 
        from bzrlib.commit import commit
796
 
 
797
 
        ## Warning: shadows builtin file()
798
 
        if not message and not file:
799
 
            raise BzrCommandError("please specify a commit message",
800
 
                                  ["use either --message or --file"])
801
 
        elif message and file:
802
 
            raise BzrCommandError("please specify either --message or --file")
803
 
        
804
 
        if file:
805
 
            import codecs
806
 
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
807
 
 
808
 
        b = Branch('.')
809
 
        commit(b, message, verbose=verbose, specific_files=selected_list)
 
605
    takes_options = ['message', 'verbose']
 
606
    
 
607
    def run(self, message=None, verbose=False):
 
608
        if not message:
 
609
            raise BzrCommandError("please specify a commit message")
 
610
        Branch('.').commit(message, verbose=verbose)
810
611
 
811
612
 
812
613
class cmd_check(Command):
882
683
    def run(self):
883
684
        print "it sure does!"
884
685
 
885
 
def parse_spec(spec):
886
 
    if '/@' in spec:
887
 
        parsed = spec.split('/@')
888
 
        assert len(parsed) == 2
889
 
        if parsed[1] == "":
890
 
            parsed[1] = -1
891
 
        else:
892
 
            parsed[1] = int(parsed[1])
893
 
            assert parsed[1] >=0
894
 
    else:
895
 
        parsed = [spec, None]
896
 
    return parsed
897
 
 
898
 
class cmd_merge(Command):
899
 
    """Perform a three-way merge of trees."""
900
 
    takes_args = ['other_spec', 'base_spec']
901
 
 
902
 
    def run(self, other_spec, base_spec):
903
 
        merge.merge(parse_spec(other_spec), parse_spec(base_spec))
904
686
 
905
687
class cmd_assert_fail(Command):
906
688
    """Test reporting of assertion failures"""
914
696
 
915
697
    For a list of all available commands, say 'bzr help commands'."""
916
698
    takes_args = ['topic?']
917
 
    aliases = ['?']
918
699
    
919
700
    def run(self, topic=None):
920
 
        import help
921
 
        help.help(topic)
922
 
 
923
 
 
924
 
class cmd_update_stat_cache(Command):
925
 
    """Update stat-cache mapping inodes to SHA-1 hashes.
926
 
 
927
 
    For testing only."""
928
 
    hidden = True
929
 
    def run(self):
930
 
        import statcache
931
 
        b = Branch('.')
932
 
        statcache.update_cache(b.base, b.read_working_inventory())
933
 
 
 
701
        help(topic)
 
702
 
 
703
 
 
704
def help(topic=None):
 
705
    if topic == None:
 
706
        print __doc__
 
707
    elif topic == 'commands':
 
708
        help_commands()
 
709
    else:
 
710
        help_on_command(topic)
 
711
 
 
712
 
 
713
def help_on_command(cmdname):
 
714
    cmdname = str(cmdname)
 
715
 
 
716
    from inspect import getdoc
 
717
    topic, cmdclass = get_cmd_class(cmdname)
 
718
 
 
719
    doc = getdoc(cmdclass)
 
720
    if doc == None:
 
721
        raise NotImplementedError("sorry, no detailed help yet for %r" % cmdname)
 
722
 
 
723
    if '\n' in doc:
 
724
        short, rest = doc.split('\n', 1)
 
725
    else:
 
726
        short = doc
 
727
        rest = ''
 
728
 
 
729
    print 'usage: bzr ' + topic,
 
730
    for aname in cmdclass.takes_args:
 
731
        aname = aname.upper()
 
732
        if aname[-1] in ['$', '+']:
 
733
            aname = aname[:-1] + '...'
 
734
        elif aname[-1] == '?':
 
735
            aname = '[' + aname[:-1] + ']'
 
736
        elif aname[-1] == '*':
 
737
            aname = '[' + aname[:-1] + '...]'
 
738
        print aname,
 
739
    print 
 
740
    print short
 
741
    if rest:
 
742
        print rest
 
743
 
 
744
    help_on_option(cmdclass.takes_options)
 
745
 
 
746
 
 
747
def help_on_option(options):
 
748
    if not options:
 
749
        return
 
750
    
 
751
    print
 
752
    print 'options:'
 
753
    for on in options:
 
754
        l = '    --' + on
 
755
        for shortname, longname in SHORT_OPTIONS.items():
 
756
            if longname == on:
 
757
                l += ', -' + shortname
 
758
                break
 
759
        print l
 
760
 
 
761
 
 
762
def help_commands():
 
763
    """List all commands"""
 
764
    import inspect
 
765
    
 
766
    accu = []
 
767
    for k, v in globals().items():
 
768
        if k.startswith('cmd_'):
 
769
            accu.append((k[4:].replace('_','-'), v))
 
770
    accu.sort()
 
771
    for cmdname, cmdclass in accu:
 
772
        if cmdclass.hidden:
 
773
            continue
 
774
        print cmdname
 
775
        help = inspect.getdoc(cmdclass)
 
776
        if help:
 
777
            print "    " + help.split('\n', 1)[0]
 
778
            
934
779
 
935
780
######################################################################
936
781
# main routine
942
787
OPTIONS = {
943
788
    'all':                    None,
944
789
    'help':                   None,
945
 
    'file':                   unicode,
946
790
    'message':                unicode,
947
791
    'profile':                None,
948
792
    'revision':               int,
955
799
 
956
800
SHORT_OPTIONS = {
957
801
    'm':                      'message',
958
 
    'F':                      'file', 
959
802
    'r':                      'revision',
960
803
    'v':                      'verbose',
961
804
}
1078
921
    This is similar to main(), but without all the trappings for
1079
922
    logging and error handling.  
1080
923
    """
 
924
 
1081
925
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1082
926
    
1083
927
    try:
1084
928
        args, opts = parse_args(argv[1:])
1085
929
        if 'help' in opts:
1086
 
            import help
1087
930
            if args:
1088
 
                help.help(args[0])
 
931
                help(args[0])
1089
932
            else:
1090
 
                help.help()
 
933
                help()
1091
934
            return 0
1092
935
        elif 'version' in opts:
1093
 
            show_version()
 
936
            cmd_version([], [])
1094
937
            return 0
1095
938
        cmd = str(args.pop(0))
1096
939
    except IndexError:
1097
 
        import help
1098
 
        help.help()
 
940
        log_error('usage: bzr COMMAND')
 
941
        log_error('  try "bzr help"')
1099
942
        return 1
1100
 
          
1101
943
 
1102
944
    canonical_cmd, cmd_class = get_cmd_class(cmd)
1103
945
 
1112
954
    allowed = cmd_class.takes_options
1113
955
    for oname in opts:
1114
956
        if oname not in allowed:
1115
 
            raise BzrCommandError("option '--%s' is not allowed for command %r"
 
957
            raise BzrCommandError("option %r is not allowed for command %r"
1116
958
                                  % (oname, cmd))
1117
959
 
1118
960
    # mix arguments and options into one dictionary
1122
964
        cmdopts[k.replace('-', '_')] = v
1123
965
 
1124
966
    if profile:
1125
 
        import hotshot, tempfile
 
967
        import hotshot
1126
968
        pffileno, pfname = tempfile.mkstemp()
1127
969
        try:
1128
970
            prof = hotshot.Profile(pfname)
1137
979
            ## print_stats seems hardcoded to stdout
1138
980
            stats.print_stats(20)
1139
981
            
1140
 
            return ret.status
 
982
            return ret
1141
983
 
1142
984
        finally:
1143
985
            os.close(pffileno)
1144
986
            os.remove(pfname)
1145
987
    else:
1146
 
        cmdobj = cmd_class(cmdopts, cmdargs).status 
1147
 
 
1148
 
 
1149
 
def _report_exception(summary, quiet=False):
 
988
        cmdobj = cmd_class(cmdopts, cmdargs) or 0
 
989
 
 
990
 
 
991
 
 
992
def _report_exception(e, summary, quiet=False):
1150
993
    import traceback
1151
994
    log_error('bzr: ' + summary)
1152
 
    bzrlib.trace.log_exception()
 
995
    bzrlib.trace.log_exception(e)
1153
996
 
1154
997
    if not quiet:
1155
998
        tb = sys.exc_info()[2]
1163
1006
def main(argv):
1164
1007
    import errno
1165
1008
    
1166
 
    bzrlib.open_tracefile(argv)
 
1009
    bzrlib.trace.create_tracefile(argv)
1167
1010
 
1168
1011
    try:
1169
1012
        try:
1170
 
            try:
1171
 
                return run_bzr(argv)
1172
 
            finally:
1173
 
                # do this here inside the exception wrappers to catch EPIPE
1174
 
                sys.stdout.flush()
 
1013
            ret = run_bzr(argv)
 
1014
            # do this here to catch EPIPE
 
1015
            sys.stdout.flush()
 
1016
            return ret
1175
1017
        except BzrError, e:
1176
1018
            quiet = isinstance(e, (BzrCommandError))
1177
 
            _report_exception('error: ' + e.args[0], quiet=quiet)
 
1019
            _report_exception(e, 'error: ' + e.args[0], quiet=quiet)
1178
1020
            if len(e.args) > 1:
1179
1021
                for h in e.args[1]:
1180
1022
                    # some explanation or hints
1184
1026
            msg = 'assertion failed'
1185
1027
            if str(e):
1186
1028
                msg += ': ' + str(e)
1187
 
            _report_exception(msg)
 
1029
            _report_exception(e, msg)
1188
1030
            return 2
1189
1031
        except KeyboardInterrupt, e:
1190
 
            _report_exception('interrupted', quiet=True)
 
1032
            _report_exception(e, 'interrupted', quiet=True)
1191
1033
            return 2
1192
1034
        except Exception, e:
1193
1035
            quiet = False
1194
 
            if (isinstance(e, IOError) 
1195
 
                and hasattr(e, 'errno')
1196
 
                and e.errno == errno.EPIPE):
 
1036
            if isinstance(e, IOError) and e.errno == errno.EPIPE:
1197
1037
                quiet = True
1198
1038
                msg = 'broken pipe'
1199
1039
            else:
1200
1040
                msg = str(e).rstrip('\n')
1201
 
            _report_exception(msg, quiet)
 
1041
            _report_exception(e, msg, quiet)
1202
1042
            return 2
1203
1043
    finally:
1204
1044
        bzrlib.trace.close_trace()