~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-08-23 06:52:09 UTC
  • mto: (974.1.50) (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1139.
  • Revision ID: robertc@robertcollins.net-20050823065209-81cd5962c401751b
move io redirection into each test case from the global runner

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
 
19
 
import sys, os
 
18
# TODO: Split the command framework away from the actual commands.
 
19
 
 
20
# TODO: probably should say which arguments are candidates for glob
 
21
# expansion on windows and do that at the command level.
 
22
 
 
23
import sys
 
24
import os
20
25
 
21
26
import bzrlib
22
 
from bzrlib.trace import mutter, note, log_error
 
27
from bzrlib.trace import mutter, note, log_error, warning
23
28
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
29
from bzrlib.branch import find_branch
25
30
from bzrlib import BZRDIR
51
56
    assert cmd.startswith("cmd_")
52
57
    return cmd[4:].replace('_','-')
53
58
 
 
59
 
54
60
def _parse_revision_str(revstr):
55
 
    """This handles a revision string -> revno. 
56
 
 
57
 
    There are several possibilities:
58
 
 
59
 
        '234'       -> 234
60
 
        '234:345'   -> [234, 345]
61
 
        ':234'      -> [None, 234]
62
 
        '234:'      -> [234, None]
63
 
 
64
 
    In the future we will also support:
65
 
        'uuid:blah-blah-blah'   -> ?
66
 
        'hash:blahblahblah'     -> ?
67
 
        potentially:
68
 
        'tag:mytag'             -> ?
 
61
    """This handles a revision string -> revno.
 
62
 
 
63
    This always returns a list.  The list will have one element for 
 
64
 
 
65
    It supports integers directly, but everything else it
 
66
    defers for passing to Branch.get_revision_info()
 
67
 
 
68
    >>> _parse_revision_str('234')
 
69
    [234]
 
70
    >>> _parse_revision_str('234..567')
 
71
    [234, 567]
 
72
    >>> _parse_revision_str('..')
 
73
    [None, None]
 
74
    >>> _parse_revision_str('..234')
 
75
    [None, 234]
 
76
    >>> _parse_revision_str('234..')
 
77
    [234, None]
 
78
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
79
    [234, 456, 789]
 
80
    >>> _parse_revision_str('234....789') # Error?
 
81
    [234, None, 789]
 
82
    >>> _parse_revision_str('revid:test@other.com-234234')
 
83
    ['revid:test@other.com-234234']
 
84
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
85
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
 
86
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
87
    ['revid:test@other.com-234234', 23]
 
88
    >>> _parse_revision_str('date:2005-04-12')
 
89
    ['date:2005-04-12']
 
90
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
91
    ['date:2005-04-12 12:24:33']
 
92
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
93
    ['date:2005-04-12T12:24:33']
 
94
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
95
    ['date:2005-04-12,12:24:33']
 
96
    >>> _parse_revision_str('-5..23')
 
97
    [-5, 23]
 
98
    >>> _parse_revision_str('-5')
 
99
    [-5]
 
100
    >>> _parse_revision_str('123a')
 
101
    ['123a']
 
102
    >>> _parse_revision_str('abc')
 
103
    ['abc']
69
104
    """
70
 
    if revstr.find(':') != -1:
71
 
        revs = revstr.split(':')
72
 
        if len(revs) > 2:
73
 
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
74
 
 
75
 
        if not revs[0]:
76
 
            revs[0] = None
77
 
        else:
78
 
            revs[0] = int(revs[0])
79
 
 
80
 
        if not revs[1]:
81
 
            revs[1] = None
82
 
        else:
83
 
            revs[1] = int(revs[1])
84
 
    else:
85
 
        revs = int(revstr)
 
105
    import re
 
106
    old_format_re = re.compile('\d*:\d*')
 
107
    m = old_format_re.match(revstr)
 
108
    if m:
 
109
        warning('Colon separator for revision numbers is deprecated.'
 
110
                ' Use .. instead')
 
111
        revs = []
 
112
        for rev in revstr.split(':'):
 
113
            if rev:
 
114
                revs.append(int(rev))
 
115
            else:
 
116
                revs.append(None)
 
117
        return revs
 
118
    revs = []
 
119
    for x in revstr.split('..'):
 
120
        if not x:
 
121
            revs.append(None)
 
122
        else:
 
123
            try:
 
124
                revs.append(int(x))
 
125
            except ValueError:
 
126
                revs.append(x)
86
127
    return revs
87
128
 
88
129
 
 
130
def get_merge_type(typestring):
 
131
    """Attempt to find the merge class/factory associated with a string."""
 
132
    from merge import merge_types
 
133
    try:
 
134
        return merge_types[typestring][0]
 
135
    except KeyError:
 
136
        templ = '%s%%7s: %%s' % (' '*12)
 
137
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
138
        type_list = '\n'.join(lines)
 
139
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
140
            (typestring, type_list)
 
141
        raise BzrCommandError(msg)
 
142
    
 
143
 
 
144
def get_merge_type(typestring):
 
145
    """Attempt to find the merge class/factory associated with a string."""
 
146
    from merge import merge_types
 
147
    try:
 
148
        return merge_types[typestring][0]
 
149
    except KeyError:
 
150
        templ = '%s%%7s: %%s' % (' '*12)
 
151
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
152
        type_list = '\n'.join(lines)
 
153
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
154
            (typestring, type_list)
 
155
        raise BzrCommandError(msg)
 
156
    
 
157
 
89
158
 
90
159
def _get_cmd_dict(plugins_override=True):
91
160
    d = {}
164
233
        assert isinstance(arguments, dict)
165
234
        cmdargs = options.copy()
166
235
        cmdargs.update(arguments)
167
 
        assert self.__doc__ != Command.__doc__, \
168
 
               ("No help message set for %r" % self)
 
236
        if self.__doc__ == Command.__doc__:
 
237
            from warnings import warn
 
238
            warn("No help message set for %r" % self)
169
239
        self.status = self.run(**cmdargs)
 
240
        if self.status is None:
 
241
            self.status = 0
170
242
 
171
243
    
172
244
    def run(self):
184
256
class ExternalCommand(Command):
185
257
    """Class to wrap external commands.
186
258
 
187
 
    We cheat a little here, when get_cmd_class() calls us we actually give it back
188
 
    an object we construct that has the appropriate path, help, options etc for the
189
 
    specified command.
190
 
 
191
 
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
192
 
    method, which we override to call the Command.__init__ method. That then calls
193
 
    our run method which is pretty straight forward.
194
 
 
195
 
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
196
 
    back into command line options and arguments for the script.
 
259
    We cheat a little here, when get_cmd_class() calls us we actually
 
260
    give it back an object we construct that has the appropriate path,
 
261
    help, options etc for the specified command.
 
262
 
 
263
    When run_bzr() tries to instantiate that 'class' it gets caught by
 
264
    the __call__ method, which we override to call the Command.__init__
 
265
    method. That then calls our run method which is pretty straight
 
266
    forward.
 
267
 
 
268
    The only wrinkle is that we have to map bzr's dictionary of options
 
269
    and arguments back into command line options and arguments for the
 
270
    script.
197
271
    """
198
272
 
199
273
    def find_command(cls, cmd):
296
370
    directory is shown.  Otherwise, only the status of the specified
297
371
    files or directories is reported.  If a directory is given, status
298
372
    is reported for everything inside that directory.
 
373
 
 
374
    If a revision is specified, the changes since that revision are shown.
299
375
    """
300
376
    takes_args = ['file*']
301
 
    takes_options = ['all', 'show-ids']
 
377
    takes_options = ['all', 'show-ids', 'revision']
302
378
    aliases = ['st', 'stat']
303
379
    
304
380
    def run(self, all=False, show_ids=False, file_list=None):
311
387
                file_list = None
312
388
        else:
313
389
            b = find_branch('.')
314
 
        import status
315
 
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
316
 
                           specific_files=file_list)
 
390
            
 
391
        from bzrlib.status import show_status
 
392
        show_status(b, show_unchanged=all, show_ids=show_ids,
 
393
                    specific_files=file_list)
317
394
 
318
395
 
319
396
class cmd_cat_revision(Command):
334
411
    def run(self):
335
412
        print find_branch('.').revno()
336
413
 
 
414
class cmd_revision_info(Command):
 
415
    """Show revision number and revision id for a given revision identifier.
 
416
    """
 
417
    hidden = True
 
418
    takes_args = ['revision_info*']
 
419
    takes_options = ['revision']
 
420
    def run(self, revision=None, revision_info_list=None):
 
421
        from bzrlib.branch import find_branch
 
422
 
 
423
        revs = []
 
424
        if revision is not None:
 
425
            revs.extend(revision)
 
426
        if revision_info_list is not None:
 
427
            revs.extend(revision_info_list)
 
428
        if len(revs) == 0:
 
429
            raise BzrCommandError('You must supply a revision identifier')
 
430
 
 
431
        b = find_branch('.')
 
432
 
 
433
        for rev in revs:
 
434
            print '%4d %s' % b.get_revision_info(rev)
 
435
 
337
436
    
338
437
class cmd_add(Command):
339
438
    """Add specified files or directories.
348
447
    whether already versioned or not, are searched for files or
349
448
    subdirectories that are neither versioned or ignored, and these
350
449
    are added.  This search proceeds recursively into versioned
351
 
    directories.
 
450
    directories.  If no names are given '.' is assumed.
352
451
 
353
 
    Therefore simply saying 'bzr add .' will version all files that
 
452
    Therefore simply saying 'bzr add' will version all files that
354
453
    are currently unknown.
355
454
 
356
455
    TODO: Perhaps adding a file whose directly is not versioned should
357
456
    recursively add that parent, rather than giving an error?
358
457
    """
359
 
    takes_args = ['file+']
 
458
    takes_args = ['file*']
360
459
    takes_options = ['verbose', 'no-recurse']
361
460
    
362
461
    def run(self, file_list, verbose=False, no_recurse=False):
401
500
        if revision == None:
402
501
            inv = b.read_working_inventory()
403
502
        else:
404
 
            inv = b.get_revision_inventory(b.lookup_revision(revision))
 
503
            if len(revision) > 1:
 
504
                raise BzrCommandError('bzr inventory --revision takes'
 
505
                    ' exactly one revision identifier')
 
506
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
405
507
 
406
508
        for path, entry in inv.entries():
407
509
            if show_ids:
422
524
    def run(self, source_list, dest):
423
525
        b = find_branch('.')
424
526
 
 
527
        # TODO: glob expansion on windows?
425
528
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
426
529
 
427
530
 
447
550
 
448
551
 
449
552
 
 
553
class cmd_mv(Command):
 
554
    """Move or rename a file.
 
555
 
 
556
    usage:
 
557
        bzr mv OLDNAME NEWNAME
 
558
        bzr mv SOURCE... DESTINATION
 
559
 
 
560
    If the last argument is a versioned directory, all the other names
 
561
    are moved into it.  Otherwise, there must be exactly two arguments
 
562
    and the file is changed to a new name, which must not already exist.
 
563
 
 
564
    Files cannot be moved between branches.
 
565
    """
 
566
    takes_args = ['names*']
 
567
    def run(self, names_list):
 
568
        if len(names_list) < 2:
 
569
            raise BzrCommandError("missing file argument")
 
570
        b = find_branch(names_list[0])
 
571
 
 
572
        rel_names = [b.relpath(x) for x in names_list]
 
573
        
 
574
        if os.path.isdir(names_list[-1]):
 
575
            # move into existing directory
 
576
            b.move(rel_names[:-1], rel_names[-1])
 
577
        else:
 
578
            if len(names_list) != 2:
 
579
                raise BzrCommandError('to mv multiple files the destination '
 
580
                                      'must be a versioned directory')
 
581
            b.move(rel_names[0], rel_names[1])
 
582
            
 
583
    
450
584
 
451
585
 
452
586
class cmd_pull(Command):
519
653
    """
520
654
    takes_args = ['from_location', 'to_location?']
521
655
    takes_options = ['revision']
 
656
    aliases = ['get', 'clone']
522
657
 
523
658
    def run(self, from_location, to_location=None, revision=None):
524
659
        import errno
525
660
        from bzrlib.merge import merge
526
 
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
661
        from bzrlib.branch import DivergedBranches, \
527
662
             find_cached_branch, Branch
528
663
        from shutil import rmtree
529
664
        from meta_store import CachedStore
530
665
        import tempfile
531
666
        cache_root = tempfile.mkdtemp()
 
667
 
 
668
        if revision is None:
 
669
            revision = [None]
 
670
        elif len(revision) > 1:
 
671
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
 
672
 
532
673
        try:
533
674
            try:
534
675
                br_from = find_cached_branch(from_location, cache_root)
555
696
                    raise
556
697
            br_to = Branch(to_location, init=True)
557
698
 
558
 
            try:
559
 
                br_to.update_revisions(br_from, stop_revision=revision)
560
 
            except NoSuchRevision:
561
 
                rmtree(to_location)
562
 
                msg = "The branch %s has no revision %d." % (from_location,
563
 
                                                             revision)
564
 
                raise BzrCommandError(msg)
 
699
            br_to.set_root_id(br_from.get_root_id())
 
700
 
 
701
            if revision:
 
702
                if revision[0] is None:
 
703
                    revno = br_from.revno()
 
704
                else:
 
705
                    revno, rev_id = br_from.get_revision_info(revision[0])
 
706
                try:
 
707
                    br_to.update_revisions(br_from, stop_revision=revno)
 
708
                except bzrlib.errors.NoSuchRevision:
 
709
                    rmtree(to_location)
 
710
                    msg = "The branch %s has no revision %d." % (from_location,
 
711
                                                                 revno)
 
712
                    raise BzrCommandError(msg)
 
713
            
565
714
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
566
715
                  check_clean=False, ignore_zero=True)
567
716
            from_location = pull_loc(br_from)
702
851
    If files are listed, only the changes in those files are listed.
703
852
    Otherwise, all changes for the tree are listed.
704
853
 
705
 
    TODO: Given two revision arguments, show the difference between them.
706
 
 
707
854
    TODO: Allow diff across branches.
708
855
 
709
856
    TODO: Option to use external diff command; could be GNU diff, wdiff,
718
865
          deleted files.
719
866
 
720
867
    TODO: This probably handles non-Unix newlines poorly.
 
868
 
 
869
    examples:
 
870
        bzr diff
 
871
        bzr diff -r1
 
872
        bzr diff -r1:2
721
873
    """
722
874
    
723
875
    takes_args = ['file*']
735
887
                file_list = None
736
888
        else:
737
889
            b = find_branch('.')
738
 
    
739
 
        show_diff(b, revision, specific_files=file_list,
740
 
                  external_diff_options=diff_options)
741
890
 
 
891
        if revision is not None:
 
892
            if len(revision) == 1:
 
893
                show_diff(b, revision[0], specific_files=file_list,
 
894
                          external_diff_options=diff_options)
 
895
            elif len(revision) == 2:
 
896
                show_diff(b, revision[0], specific_files=file_list,
 
897
                          external_diff_options=diff_options,
 
898
                          revision2=revision[1])
 
899
            else:
 
900
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
 
901
        else:
 
902
            show_diff(b, None, specific_files=file_list,
 
903
                      external_diff_options=diff_options)
742
904
 
743
905
        
744
906
 
770
932
    """List files modified in working tree."""
771
933
    hidden = True
772
934
    def run(self):
773
 
        import statcache
 
935
        from bzrlib.delta import compare_trees
 
936
 
774
937
        b = find_branch('.')
775
 
        inv = b.read_working_inventory()
776
 
        sc = statcache.update_cache(b, inv)
777
 
        basis = b.basis_tree()
778
 
        basis_inv = basis.inventory
779
 
        
780
 
        # We used to do this through iter_entries(), but that's slow
781
 
        # when most of the files are unmodified, as is usually the
782
 
        # case.  So instead we iterate by inventory entry, and only
783
 
        # calculate paths as necessary.
 
938
        td = compare_trees(b.basis_tree(), b.working_tree())
784
939
 
785
 
        for file_id in basis_inv:
786
 
            cacheentry = sc.get(file_id)
787
 
            if not cacheentry:                 # deleted
788
 
                continue
789
 
            ie = basis_inv[file_id]
790
 
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
791
 
                path = inv.id2path(file_id)
792
 
                print path
 
940
        for path, id, kind in td.modified:
 
941
            print path
793
942
 
794
943
 
795
944
 
830
979
    -r revision requests a specific revision, -r :end or -r begin: are
831
980
    also valid.
832
981
 
 
982
    --message allows you to give a regular expression, which will be evaluated
 
983
    so that only matching entries will be displayed.
 
984
 
833
985
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
834
986
  
835
987
    """
836
988
 
837
989
    takes_args = ['filename?']
838
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long']
 
990
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
 
991
                     'long', 'message', 'short',]
839
992
    
840
993
    def run(self, filename=None, timezone='original',
841
994
            verbose=False,
842
995
            show_ids=False,
843
996
            forward=False,
844
997
            revision=None,
845
 
            long=False):
 
998
            message=None,
 
999
            long=False,
 
1000
            short=False):
846
1001
        from bzrlib.branch import find_branch
847
1002
        from bzrlib.log import log_formatter, show_log
848
1003
        import codecs
860
1015
            b = find_branch('.')
861
1016
            file_id = None
862
1017
 
863
 
        if revision == None:
864
 
            revision = [None, None]
865
 
        elif isinstance(revision, int):
866
 
            revision = [revision, revision]
 
1018
        if revision is None:
 
1019
            rev1 = None
 
1020
            rev2 = None
 
1021
        elif len(revision) == 1:
 
1022
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
 
1023
        elif len(revision) == 2:
 
1024
            rev1 = b.get_revision_info(revision[0])[0]
 
1025
            rev2 = b.get_revision_info(revision[1])[0]
867
1026
        else:
868
 
            # pair of revisions?
869
 
            pass
870
 
            
871
 
        assert len(revision) == 2
 
1027
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
1028
 
 
1029
        if rev1 == 0:
 
1030
            rev1 = None
 
1031
        if rev2 == 0:
 
1032
            rev2 = None
872
1033
 
873
1034
        mutter('encoding log as %r' % bzrlib.user_encoding)
874
1035
 
876
1037
        # in e.g. the default C locale.
877
1038
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
878
1039
 
879
 
        if long:
 
1040
        if not short:
880
1041
            log_format = 'long'
881
1042
        else:
882
1043
            log_format = 'short'
890
1051
                 file_id,
891
1052
                 verbose=verbose,
892
1053
                 direction=direction,
893
 
                 start_revision=revision[0],
894
 
                 end_revision=revision[1])
 
1054
                 start_revision=rev1,
 
1055
                 end_revision=rev2,
 
1056
                 search=message)
895
1057
 
896
1058
 
897
1059
 
1042
1204
    If no revision is specified this exports the last committed revision.
1043
1205
 
1044
1206
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1045
 
    given, exports to a directory (equivalent to --format=dir)."""
 
1207
    given, try to find the format with the extension. If no extension
 
1208
    is found exports to a directory (equivalent to --format=dir).
 
1209
 
 
1210
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1211
    is given, the top directory will be the root name of the file."""
1046
1212
    # TODO: list known exporters
1047
1213
    takes_args = ['dest']
1048
 
    takes_options = ['revision', 'format']
1049
 
    def run(self, dest, revision=None, format='dir'):
 
1214
    takes_options = ['revision', 'format', 'root']
 
1215
    def run(self, dest, revision=None, format=None, root=None):
 
1216
        import os.path
1050
1217
        b = find_branch('.')
1051
 
        if revision == None:
1052
 
            rh = b.revision_history()[-1]
 
1218
        if revision is None:
 
1219
            rev_id = b.last_patch()
1053
1220
        else:
1054
 
            rh = b.lookup_revision(int(revision))
1055
 
        t = b.revision_tree(rh)
1056
 
        t.export(dest, format)
 
1221
            if len(revision) != 1:
 
1222
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
1223
            revno, rev_id = b.get_revision_info(revision[0])
 
1224
        t = b.revision_tree(rev_id)
 
1225
        root, ext = os.path.splitext(dest)
 
1226
        if not format:
 
1227
            if ext in (".tar",):
 
1228
                format = "tar"
 
1229
            elif ext in (".gz", ".tgz"):
 
1230
                format = "tgz"
 
1231
            elif ext in (".bz2", ".tbz2"):
 
1232
                format = "tbz2"
 
1233
            else:
 
1234
                format = "dir"
 
1235
        t.export(dest, format, root)
1057
1236
 
1058
1237
 
1059
1238
class cmd_cat(Command):
1065
1244
    def run(self, filename, revision=None):
1066
1245
        if revision == None:
1067
1246
            raise BzrCommandError("bzr cat requires a revision number")
 
1247
        elif len(revision) != 1:
 
1248
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1068
1249
        b = find_branch('.')
1069
 
        b.print_file(b.relpath(filename), int(revision))
 
1250
        b.print_file(b.relpath(filename), revision[0])
1070
1251
 
1071
1252
 
1072
1253
class cmd_local_time_offset(Command):
1079
1260
 
1080
1261
class cmd_commit(Command):
1081
1262
    """Commit changes into a new revision.
 
1263
    
 
1264
    If no arguments are given, the entire tree is committed.
1082
1265
 
1083
1266
    If selected files are specified, only changes to those files are
1084
 
    committed.  If a directory is specified then its contents are also
1085
 
    committed.
 
1267
    committed.  If a directory is specified then the directory and everything 
 
1268
    within it is committed.
1086
1269
 
1087
1270
    A selected-file commit may fail in some cases where the committed
1088
1271
    tree would be invalid, such as trying to commit a file in a
1093
1276
    TODO: Strict commit that fails if there are unknown or deleted files.
1094
1277
    """
1095
1278
    takes_args = ['selected*']
1096
 
    takes_options = ['message', 'file', 'verbose']
 
1279
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1097
1280
    aliases = ['ci', 'checkin']
1098
1281
 
1099
 
    def run(self, message=None, file=None, verbose=True, selected_list=None):
1100
 
        from bzrlib.commit import commit
 
1282
    # TODO: Give better message for -s, --summary, used by tla people
 
1283
    
 
1284
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1285
            unchanged=False):
 
1286
        from bzrlib.errors import PointlessCommit
1101
1287
        from bzrlib.osutils import get_text_message
1102
1288
 
1103
1289
        ## Warning: shadows builtin file()
1104
1290
        if not message and not file:
 
1291
            # FIXME: Ugly; change status code to send to a provided function?
 
1292
            
1105
1293
            import cStringIO
1106
1294
            stdout = sys.stdout
1107
1295
            catcher = cStringIO.StringIO()
1122
1310
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1123
1311
 
1124
1312
        b = find_branch('.')
1125
 
        commit(b, message, verbose=verbose, specific_files=selected_list)
 
1313
        if selected_list:
 
1314
            selected_list = [b.relpath(s) for s in selected_list]
 
1315
            
 
1316
        try:
 
1317
            b.commit(message, verbose=verbose,
 
1318
                     specific_files=selected_list,
 
1319
                     allow_pointless=unchanged)
 
1320
        except PointlessCommit:
 
1321
            # FIXME: This should really happen before the file is read in;
 
1322
            # perhaps prepare the commit; get the message; then actually commit
 
1323
            raise BzrCommandError("no changes to commit",
 
1324
                                  ["use --unchanged to commit anyhow"])
1126
1325
 
1127
1326
 
1128
1327
class cmd_check(Command):
1142
1341
 
1143
1342
 
1144
1343
 
 
1344
class cmd_scan_cache(Command):
 
1345
    hidden = True
 
1346
    def run(self):
 
1347
        from bzrlib.hashcache import HashCache
 
1348
        import os
 
1349
 
 
1350
        c = HashCache('.')
 
1351
        c.read()
 
1352
        c.scan()
 
1353
            
 
1354
        print '%6d stats' % c.stat_count
 
1355
        print '%6d in hashcache' % len(c._cache)
 
1356
        print '%6d files removed from cache' % c.removed_count
 
1357
        print '%6d hashes updated' % c.update_count
 
1358
        print '%6d files changed too recently to cache' % c.danger_count
 
1359
 
 
1360
        if c.needs_write:
 
1361
            c.write()
 
1362
            
 
1363
 
 
1364
 
1145
1365
class cmd_upgrade(Command):
1146
1366
    """Upgrade branch storage to current format.
1147
1367
 
1161
1381
    takes_options = ['email']
1162
1382
    
1163
1383
    def run(self, email=False):
 
1384
        try:
 
1385
            b = bzrlib.branch.find_branch('.')
 
1386
        except:
 
1387
            b = None
 
1388
        
1164
1389
        if email:
1165
 
            print bzrlib.osutils.user_email()
 
1390
            print bzrlib.osutils.user_email(b)
1166
1391
        else:
1167
 
            print bzrlib.osutils.username()
 
1392
            print bzrlib.osutils.username(b)
1168
1393
 
1169
1394
 
1170
1395
class cmd_selftest(Command):
1171
1396
    """Run internal test suite"""
1172
1397
    hidden = True
1173
 
    def run(self):
 
1398
    takes_options = ['verbose']
 
1399
    def run(self, verbose=False):
1174
1400
        from bzrlib.selftest import selftest
1175
 
        return int(not selftest())
 
1401
        return int(not selftest(verbose=verbose))
1176
1402
 
1177
1403
 
1178
1404
class cmd_version(Command):
1210
1436
    ['..', -1]
1211
1437
    >>> parse_spec("../f/@35")
1212
1438
    ['../f', 35]
 
1439
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
1440
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1213
1441
    """
1214
1442
    if spec is None:
1215
1443
        return [None, None]
1219
1447
        if parsed[1] == "":
1220
1448
            parsed[1] = -1
1221
1449
        else:
1222
 
            parsed[1] = int(parsed[1])
1223
 
            assert parsed[1] >=0
 
1450
            try:
 
1451
                parsed[1] = int(parsed[1])
 
1452
            except ValueError:
 
1453
                pass # We can allow stuff like ./@revid:blahblahblah
 
1454
            else:
 
1455
                assert parsed[1] >=0
1224
1456
    else:
1225
1457
        parsed = [spec, None]
1226
1458
    return parsed
1228
1460
 
1229
1461
 
1230
1462
class cmd_merge(Command):
1231
 
    """Perform a three-way merge of trees.
1232
 
    
1233
 
    The SPEC parameters are working tree or revision specifiers.  Working trees
1234
 
    are specified using standard paths or urls.  No component of a directory
1235
 
    path may begin with '@'.
1236
 
    
1237
 
    Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1238
 
 
1239
 
    Revisions are specified using a dirname/@revno pair, where dirname is the
1240
 
    branch directory and revno is the revision within that branch.  If no revno
1241
 
    is specified, the latest revision is used.
1242
 
 
1243
 
    Revision examples: './@127', 'foo/@', '../@1'
1244
 
 
1245
 
    The OTHER_SPEC parameter is required.  If the BASE_SPEC parameter is
1246
 
    not supplied, the common ancestor of OTHER_SPEC the current branch is used
1247
 
    as the BASE.
1248
 
 
 
1463
    """Perform a three-way merge.
 
1464
    
 
1465
    The branch is the branch you will merge from.  By default, it will merge
 
1466
    the latest revision.  If you specify a revision, that revision will be
 
1467
    merged.  If you specify two revisions, the first will be used as a BASE, 
 
1468
    and the second one as OTHER.  Revision numbers are always relative to the
 
1469
    specified branch.
 
1470
    
 
1471
    Examples:
 
1472
 
 
1473
    To merge the latest revision from bzr.dev
 
1474
    bzr merge ../bzr.dev
 
1475
 
 
1476
    To merge changes up to and including revision 82 from bzr.dev
 
1477
    bzr merge -r 82 ../bzr.dev
 
1478
 
 
1479
    To merge the changes introduced by 82, without previous changes:
 
1480
    bzr merge -r 81..82 ../bzr.dev
 
1481
    
1249
1482
    merge refuses to run if there are any uncommitted changes, unless
1250
1483
    --force is given.
1251
1484
    """
1252
 
    takes_args = ['other_spec', 'base_spec?']
1253
 
    takes_options = ['force']
 
1485
    takes_args = ['branch?']
 
1486
    takes_options = ['revision', 'force', 'merge-type']
1254
1487
 
1255
 
    def run(self, other_spec, base_spec=None, force=False):
 
1488
    def run(self, branch='.', revision=None, force=False, 
 
1489
            merge_type=None):
1256
1490
        from bzrlib.merge import merge
1257
 
        merge(parse_spec(other_spec), parse_spec(base_spec),
1258
 
              check_clean=(not force))
 
1491
        from bzrlib.merge_core import ApplyMerge3
 
1492
        if merge_type is None:
 
1493
            merge_type = ApplyMerge3
1259
1494
 
 
1495
        if revision is None or len(revision) < 1:
 
1496
            base = (None, None)
 
1497
            other = (branch, -1)
 
1498
        else:
 
1499
            if len(revision) == 1:
 
1500
                other = (branch, revision[0])
 
1501
                base = (None, None)
 
1502
            else:
 
1503
                assert len(revision) == 2
 
1504
                if None in revision:
 
1505
                    raise BzrCommandError(
 
1506
                        "Merge doesn't permit that revision specifier.")
 
1507
                base = (branch, revision[0])
 
1508
                other = (branch, revision[1])
 
1509
            
 
1510
        merge(other, base, check_clean=(not force), merge_type=merge_type)
1260
1511
 
1261
1512
 
1262
1513
class cmd_revert(Command):
1263
 
    """Restore selected files from a previous revision.
1264
 
    """
1265
 
    takes_args = ['file+']
1266
 
    def run(self, file_list):
1267
 
        from bzrlib.branch import find_branch
1268
 
        
1269
 
        if not file_list:
1270
 
            file_list = ['.']
1271
 
            
1272
 
        b = find_branch(file_list[0])
1273
 
 
1274
 
        b.revert([b.relpath(f) for f in file_list])
1275
 
 
1276
 
 
1277
 
class cmd_merge_revert(Command):
1278
1514
    """Reverse all changes since the last commit.
1279
1515
 
1280
 
    Only versioned files are affected.
1281
 
 
1282
 
    TODO: Store backups of any files that will be reverted, so
1283
 
          that the revert can be undone.          
 
1516
    Only versioned files are affected.  Specify filenames to revert only 
 
1517
    those files.  By default, any files that are changed will be backed up
 
1518
    first.  Backup files have a '~' appended to their name.
1284
1519
    """
1285
 
    takes_options = ['revision']
 
1520
    takes_options = ['revision', 'no-backup']
 
1521
    takes_args = ['file*']
 
1522
    aliases = ['merge-revert']
1286
1523
 
1287
 
    def run(self, revision=-1):
 
1524
    def run(self, revision=None, no_backup=False, file_list=None):
1288
1525
        from bzrlib.merge import merge
1289
 
        merge(('.', revision), parse_spec('.'),
 
1526
        if file_list is not None:
 
1527
            if len(file_list) == 0:
 
1528
                raise BzrCommandError("No files specified")
 
1529
        if revision is None:
 
1530
            revision = [-1]
 
1531
        elif len(revision) != 1:
 
1532
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
 
1533
        merge(('.', revision[0]), parse_spec('.'),
1290
1534
              check_clean=False,
1291
 
              ignore_zero=True)
 
1535
              ignore_zero=True,
 
1536
              backup_files=not no_backup,
 
1537
              file_list=file_list)
1292
1538
 
1293
1539
 
1294
1540
class cmd_assert_fail(Command):
1310
1556
        help.help(topic)
1311
1557
 
1312
1558
 
1313
 
class cmd_update_stat_cache(Command):
1314
 
    """Update stat-cache mapping inodes to SHA-1 hashes.
 
1559
class cmd_shell_complete(Command):
 
1560
    """Show appropriate completions for context.
1315
1561
 
1316
 
    For testing only."""
 
1562
    For a list of all available commands, say 'bzr shell-complete'."""
 
1563
    takes_args = ['context?']
 
1564
    aliases = ['s-c']
1317
1565
    hidden = True
1318
 
    def run(self):
1319
 
        import statcache
 
1566
    
 
1567
    def run(self, context=None):
 
1568
        import shellcomplete
 
1569
        shellcomplete.shellcomplete(context)
 
1570
 
 
1571
 
 
1572
class cmd_missing(Command):
 
1573
    """What is missing in this branch relative to other branch.
 
1574
    """
 
1575
    takes_args = ['remote?']
 
1576
    aliases = ['mis', 'miss']
 
1577
    # We don't have to add quiet to the list, because 
 
1578
    # unknown options are parsed as booleans
 
1579
    takes_options = ['verbose', 'quiet']
 
1580
 
 
1581
    def run(self, remote=None, verbose=False, quiet=False):
 
1582
        from bzrlib.branch import find_branch, DivergedBranches
 
1583
        from bzrlib.errors import BzrCommandError
 
1584
        from bzrlib.missing import get_parent, show_missing
 
1585
 
 
1586
        if verbose and quiet:
 
1587
            raise BzrCommandError('Cannot pass both quiet and verbose')
 
1588
 
1320
1589
        b = find_branch('.')
1321
 
        statcache.update_cache(b.base, b.read_working_inventory())
 
1590
        parent = get_parent(b)
 
1591
        if remote is None:
 
1592
            if parent is None:
 
1593
                raise BzrCommandError("No missing location known or specified.")
 
1594
            else:
 
1595
                if not quiet:
 
1596
                    print "Using last location: %s" % parent
 
1597
                remote = parent
 
1598
        elif parent is None:
 
1599
            # We only update x-pull if it did not exist, missing should not change the parent
 
1600
            b.controlfile('x-pull', 'wb').write(remote + '\n')
 
1601
        br_remote = find_branch(remote)
1322
1602
 
 
1603
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1323
1604
 
1324
1605
 
1325
1606
class cmd_plugins(Command):
1327
1608
    hidden = True
1328
1609
    def run(self):
1329
1610
        import bzrlib.plugin
 
1611
        from inspect import getdoc
1330
1612
        from pprint import pprint
1331
 
        pprint(bzrlib.plugin.all_plugins)
 
1613
        for plugin in bzrlib.plugin.all_plugins:
 
1614
            print plugin.__path__[0]
 
1615
            d = getdoc(plugin)
 
1616
            if d:
 
1617
                print '\t', d.split('\n')[0]
 
1618
 
 
1619
        #pprint(bzrlib.plugin.all_plugins)
1332
1620
 
1333
1621
 
1334
1622
 
1347
1635
    'no-recurse':             None,
1348
1636
    'profile':                None,
1349
1637
    'revision':               _parse_revision_str,
 
1638
    'short':                  None,
1350
1639
    'show-ids':               None,
1351
1640
    'timezone':               str,
1352
1641
    'verbose':                None,
1353
1642
    'version':                None,
1354
1643
    'email':                  None,
 
1644
    'unchanged':              None,
1355
1645
    'update':                 None,
1356
1646
    'long':                   None,
 
1647
    'root':                   str,
 
1648
    'no-backup':              None,
 
1649
    'merge-type':             get_merge_type,
1357
1650
    }
1358
1651
 
1359
1652
SHORT_OPTIONS = {
1383
1676
    >>> parse_args('commit --message=biter'.split())
1384
1677
    (['commit'], {'message': u'biter'})
1385
1678
    >>> parse_args('log -r 500'.split())
1386
 
    (['log'], {'revision': 500})
1387
 
    >>> parse_args('log -r500:600'.split())
 
1679
    (['log'], {'revision': [500]})
 
1680
    >>> parse_args('log -r500..600'.split())
1388
1681
    (['log'], {'revision': [500, 600]})
1389
 
    >>> parse_args('log -vr500:600'.split())
 
1682
    >>> parse_args('log -vr500..600'.split())
1390
1683
    (['log'], {'verbose': True, 'revision': [500, 600]})
1391
 
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
1392
 
    Traceback (most recent call last):
1393
 
    ...
1394
 
    ValueError: invalid literal for int(): v500
 
1684
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
1685
    (['log'], {'revision': ['v500', 600]})
1395
1686
    """
1396
1687
    args = []
1397
1688
    opts = {}
1511
1802
    return argdict
1512
1803
 
1513
1804
 
1514
 
def _parse_master_args(argv):
1515
 
    """Parse the arguments that always go with the original command.
1516
 
    These are things like bzr --no-plugins, etc.
1517
 
 
1518
 
    There are now 2 types of option flags. Ones that come *before* the command,
1519
 
    and ones that come *after* the command.
1520
 
    Ones coming *before* the command are applied against all possible commands.
1521
 
    And are generally applied before plugins are loaded.
1522
 
 
1523
 
    The current list are:
1524
 
        --builtin   Allow plugins to load, but don't let them override builtin commands,
1525
 
                    they will still be allowed if they do not override a builtin.
1526
 
        --no-plugins    Don't load any plugins. This lets you get back to official source
1527
 
                        behavior.
1528
 
        --profile   Enable the hotspot profile before running the command.
1529
 
                    For backwards compatibility, this is also a non-master option.
1530
 
        --version   Spit out the version of bzr that is running and exit.
1531
 
                    This is also a non-master option.
1532
 
        --help      Run help and exit, also a non-master option (I think that should stay, though)
1533
 
 
1534
 
    >>> argv, opts = _parse_master_args(['bzr', '--test'])
1535
 
    Traceback (most recent call last):
1536
 
    ...
1537
 
    BzrCommandError: Invalid master option: 'test'
1538
 
    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
1539
 
    >>> print argv
1540
 
    ['command']
1541
 
    >>> print opts['version']
1542
 
    True
1543
 
    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
1544
 
    >>> print argv
1545
 
    ['command', '--more-options']
1546
 
    >>> print opts['profile']
1547
 
    True
1548
 
    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
1549
 
    >>> print argv
1550
 
    ['command']
1551
 
    >>> print opts['no-plugins']
1552
 
    True
1553
 
    >>> print opts['profile']
1554
 
    False
1555
 
    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
1556
 
    >>> print argv
1557
 
    ['command', '--profile']
1558
 
    >>> print opts['profile']
1559
 
    False
1560
 
    """
1561
 
    master_opts = {'builtin':False,
1562
 
        'no-plugins':False,
1563
 
        'version':False,
1564
 
        'profile':False,
1565
 
        'help':False
1566
 
    }
1567
 
 
1568
 
    # This is the point where we could hook into argv[0] to determine
1569
 
    # what front-end is supposed to be run
1570
 
    # For now, we are just ignoring it.
1571
 
    cmd_name = argv.pop(0)
1572
 
    for arg in argv[:]:
1573
 
        if arg[:2] != '--': # at the first non-option, we return the rest
1574
 
            break
1575
 
        arg = arg[2:] # Remove '--'
1576
 
        if arg not in master_opts:
1577
 
            # We could say that this is not an error, that we should
1578
 
            # just let it be handled by the main section instead
1579
 
            raise BzrCommandError('Invalid master option: %r' % arg)
1580
 
        argv.pop(0) # We are consuming this entry
1581
 
        master_opts[arg] = True
1582
 
    return argv, master_opts
1583
 
 
1584
 
 
1585
1805
 
1586
1806
def run_bzr(argv):
1587
1807
    """Execute a command.
1588
1808
 
1589
1809
    This is similar to main(), but without all the trappings for
1590
1810
    logging and error handling.  
 
1811
    
 
1812
    argv
 
1813
       The command-line arguments, without the program name from argv[0]
 
1814
    
 
1815
    Returns a command status or raises an exception.
 
1816
 
 
1817
    Special master options: these must come before the command because
 
1818
    they control how the command is interpreted.
 
1819
 
 
1820
    --no-plugins
 
1821
        Do not load plugin modules at all
 
1822
 
 
1823
    --builtin
 
1824
        Only use builtin commands.  (Plugins are still allowed to change
 
1825
        other behaviour.)
 
1826
 
 
1827
    --profile
 
1828
        Run under the Python profiler.
1591
1829
    """
 
1830
    
1592
1831
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
1832
 
 
1833
    opt_profile = opt_no_plugins = opt_builtin = False
 
1834
 
 
1835
    # --no-plugins is handled specially at a very early stage. We need
 
1836
    # to load plugins before doing other command parsing so that they
 
1837
    # can override commands, but this needs to happen first.
 
1838
 
 
1839
    for a in argv[:]:
 
1840
        if a == '--profile':
 
1841
            opt_profile = True
 
1842
        elif a == '--no-plugins':
 
1843
            opt_no_plugins = True
 
1844
        elif a == '--builtin':
 
1845
            opt_builtin = True
 
1846
        else:
 
1847
            break
 
1848
        argv.remove(a)
 
1849
 
 
1850
    if not opt_no_plugins:
 
1851
        from bzrlib.plugin import load_plugins
 
1852
        load_plugins()
 
1853
 
 
1854
    args, opts = parse_args(argv)
 
1855
 
 
1856
    if 'help' in opts:
 
1857
        from bzrlib.help import help
 
1858
        if args:
 
1859
            help(args[0])
 
1860
        else:
 
1861
            help()
 
1862
        return 0            
 
1863
        
 
1864
    if 'version' in opts:
 
1865
        show_version()
 
1866
        return 0
1593
1867
    
1594
 
    try:
1595
 
        # some options like --builtin and --no-plugins have special effects
1596
 
        argv, master_opts = _parse_master_args(argv)
1597
 
        if not master_opts['no-plugins']:
1598
 
            from bzrlib.plugin import load_plugins
1599
 
            load_plugins()
1600
 
 
1601
 
        args, opts = parse_args(argv)
1602
 
 
1603
 
        if master_opts['help']:
1604
 
            from bzrlib.help import help
1605
 
            if argv:
1606
 
                help(argv[0])
1607
 
            else:
1608
 
                help()
1609
 
            return 0            
1610
 
            
1611
 
        if 'help' in opts:
1612
 
            from bzrlib.help import help
1613
 
            if args:
1614
 
                help(args[0])
1615
 
            else:
1616
 
                help()
1617
 
            return 0
1618
 
        elif 'version' in opts:
1619
 
            show_version()
1620
 
            return 0
1621
 
        elif args and args[0] == 'builtin':
1622
 
            include_plugins=False
1623
 
            args = args[1:]
1624
 
        cmd = str(args.pop(0))
1625
 
    except IndexError:
1626
 
        import help
1627
 
        help.help()
 
1868
    if not args:
 
1869
        print >>sys.stderr, "please try 'bzr help' for help"
1628
1870
        return 1
1629
 
          
1630
 
 
1631
 
    plugins_override = not (master_opts['builtin'])
1632
 
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1633
 
 
1634
 
    profile = master_opts['profile']
1635
 
    # For backwards compatibility, I would rather stick with --profile being a
1636
 
    # master/global option
1637
 
    if 'profile' in opts:
1638
 
        profile = True
1639
 
        del opts['profile']
 
1871
    
 
1872
    cmd = str(args.pop(0))
 
1873
 
 
1874
    canonical_cmd, cmd_class = \
 
1875
                   get_cmd_class(cmd, plugins_override=not opt_builtin)
1640
1876
 
1641
1877
    # check options are reasonable
1642
1878
    allowed = cmd_class.takes_options
1651
1887
    for k, v in opts.items():
1652
1888
        cmdopts[k.replace('-', '_')] = v
1653
1889
 
1654
 
    if profile:
 
1890
    if opt_profile:
1655
1891
        import hotshot, tempfile
1656
1892
        pffileno, pfname = tempfile.mkstemp()
1657
1893
        try:
1678
1914
 
1679
1915
def _report_exception(summary, quiet=False):
1680
1916
    import traceback
 
1917
    
1681
1918
    log_error('bzr: ' + summary)
1682
1919
    bzrlib.trace.log_exception()
1683
1920
 
 
1921
    if os.environ.get('BZR_DEBUG'):
 
1922
        traceback.print_exc()
 
1923
 
1684
1924
    if not quiet:
 
1925
        sys.stderr.write('\n')
1685
1926
        tb = sys.exc_info()[2]
1686
1927
        exinfo = traceback.extract_tb(tb)
1687
1928
        if exinfo:
1697
1938
    try:
1698
1939
        try:
1699
1940
            try:
1700
 
                return run_bzr(argv)
 
1941
                return run_bzr(argv[1:])
1701
1942
            finally:
1702
1943
                # do this here inside the exception wrappers to catch EPIPE
1703
1944
                sys.stdout.flush()
1704
1945
        except BzrError, e:
1705
1946
            quiet = isinstance(e, (BzrCommandError))
1706
 
            _report_exception('error: ' + e.args[0], quiet=quiet)
 
1947
            _report_exception('error: ' + str(e), quiet=quiet)
1707
1948
            if len(e.args) > 1:
1708
1949
                for h in e.args[1]:
1709
1950
                    # some explanation or hints