~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-07-18 13:12:58 UTC
  • Revision ID: mbp@sourcefrog.net-20050718131257-e48090a91591a9cb
- use __slots__ for Weave

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
 
# 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
 
# TODO: Help messages for options.
24
 
 
25
 
# TODO: Define arguments by objects, rather than just using names.
26
 
# Those objects can specify the expected type of the argument, which
27
 
# would help with validation and shell completion.
28
 
 
29
 
 
30
 
# TODO: Help messages for options.
31
 
 
32
 
# TODO: Define arguments by objects, rather than just using names.
33
 
# Those objects can specify the expected type of the argument, which
34
 
# would help with validation and shell completion.
35
 
 
36
 
 
37
 
import sys
38
 
import os
 
18
 
 
19
import sys, os
39
20
 
40
21
import bzrlib
41
 
import bzrlib.trace
42
22
from bzrlib.trace import mutter, note, log_error, warning
43
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
44
24
from bzrlib.branch import find_branch
142
122
    return revs
143
123
 
144
124
 
145
 
def get_merge_type(typestring):
146
 
    """Attempt to find the merge class/factory associated with a string."""
147
 
    from merge import merge_types
148
 
    try:
149
 
        return merge_types[typestring][0]
150
 
    except KeyError:
151
 
        templ = '%s%%7s: %%s' % (' '*12)
152
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
153
 
        type_list = '\n'.join(lines)
154
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
155
 
            (typestring, type_list)
156
 
        raise BzrCommandError(msg)
157
 
    
158
 
 
159
 
def get_merge_type(typestring):
160
 
    """Attempt to find the merge class/factory associated with a string."""
161
 
    from merge import merge_types
162
 
    try:
163
 
        return merge_types[typestring][0]
164
 
    except KeyError:
165
 
        templ = '%s%%7s: %%s' % (' '*12)
166
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
167
 
        type_list = '\n'.join(lines)
168
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
169
 
            (typestring, type_list)
170
 
        raise BzrCommandError(msg)
171
 
    
172
 
 
173
125
 
174
126
def _get_cmd_dict(plugins_override=True):
175
127
    d = {}
248
200
        assert isinstance(arguments, dict)
249
201
        cmdargs = options.copy()
250
202
        cmdargs.update(arguments)
251
 
        if self.__doc__ == Command.__doc__:
252
 
            from warnings import warn
253
 
            warn("No help message set for %r" % self)
 
203
        assert self.__doc__ != Command.__doc__, \
 
204
               ("No help message set for %r" % self)
254
205
        self.status = self.run(**cmdargs)
255
 
        if self.status is None:
256
 
            self.status = 0
257
206
 
258
207
    
259
208
    def run(self):
271
220
class ExternalCommand(Command):
272
221
    """Class to wrap external commands.
273
222
 
274
 
    We cheat a little here, when get_cmd_class() calls us we actually
275
 
    give it back an object we construct that has the appropriate path,
276
 
    help, options etc for the specified command.
277
 
 
278
 
    When run_bzr() tries to instantiate that 'class' it gets caught by
279
 
    the __call__ method, which we override to call the Command.__init__
280
 
    method. That then calls our run method which is pretty straight
281
 
    forward.
282
 
 
283
 
    The only wrinkle is that we have to map bzr's dictionary of options
284
 
    and arguments back into command line options and arguments for the
285
 
    script.
 
223
    We cheat a little here, when get_cmd_class() calls us we actually give it back
 
224
    an object we construct that has the appropriate path, help, options etc for the
 
225
    specified command.
 
226
 
 
227
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
 
228
    method, which we override to call the Command.__init__ method. That then calls
 
229
    our run method which is pretty straight forward.
 
230
 
 
231
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
 
232
    back into command line options and arguments for the script.
286
233
    """
287
234
 
288
235
    def find_command(cls, cmd):
462
409
    whether already versioned or not, are searched for files or
463
410
    subdirectories that are neither versioned or ignored, and these
464
411
    are added.  This search proceeds recursively into versioned
465
 
    directories.  If no names are given '.' is assumed.
 
412
    directories.
466
413
 
467
 
    Therefore simply saying 'bzr add' will version all files that
 
414
    Therefore simply saying 'bzr add .' will version all files that
468
415
    are currently unknown.
469
416
 
470
417
    TODO: Perhaps adding a file whose directly is not versioned should
471
418
    recursively add that parent, rather than giving an error?
472
419
    """
473
 
    takes_args = ['file*']
 
420
    takes_args = ['file+']
474
421
    takes_options = ['verbose', 'no-recurse']
475
422
    
476
423
    def run(self, file_list, verbose=False, no_recurse=False):
477
 
        from bzrlib.add import smart_add, _PrintAddCallback
478
 
        smart_add(file_list, verbose, not no_recurse,
479
 
                  callback=_PrintAddCallback)
 
424
        from bzrlib.add import smart_add
 
425
        smart_add(file_list, verbose, not no_recurse)
480
426
 
481
427
 
482
428
 
540
486
    def run(self, source_list, dest):
541
487
        b = find_branch('.')
542
488
 
543
 
        # TODO: glob expansion on windows?
544
489
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
545
490
 
546
491
 
566
511
 
567
512
 
568
513
 
569
 
class cmd_mv(Command):
570
 
    """Move or rename a file.
571
 
 
572
 
    usage:
573
 
        bzr mv OLDNAME NEWNAME
574
 
        bzr mv SOURCE... DESTINATION
575
 
 
576
 
    If the last argument is a versioned directory, all the other names
577
 
    are moved into it.  Otherwise, there must be exactly two arguments
578
 
    and the file is changed to a new name, which must not already exist.
579
 
 
580
 
    Files cannot be moved between branches.
581
 
    """
582
 
    takes_args = ['names*']
583
 
    def run(self, names_list):
584
 
        if len(names_list) < 2:
585
 
            raise BzrCommandError("missing file argument")
586
 
        b = find_branch(names_list[0])
587
 
 
588
 
        rel_names = [b.relpath(x) for x in names_list]
589
 
        
590
 
        if os.path.isdir(names_list[-1]):
591
 
            # move into existing directory
592
 
            b.move(rel_names[:-1], rel_names[-1])
593
 
        else:
594
 
            if len(names_list) != 2:
595
 
                raise BzrCommandError('to mv multiple files the destination '
596
 
                                      'must be a versioned directory')
597
 
            b.move(rel_names[0], rel_names[1])
598
 
            
599
 
    
600
514
 
601
515
 
602
516
class cmd_pull(Command):
620
534
        import tempfile
621
535
        from shutil import rmtree
622
536
        import errno
623
 
        from bzrlib.branch import pull_loc
624
537
        
625
538
        br_to = find_branch('.')
626
539
        stored_loc = None
670
583
    """
671
584
    takes_args = ['from_location', 'to_location?']
672
585
    takes_options = ['revision']
673
 
    aliases = ['get', 'clone']
674
586
 
675
587
    def run(self, from_location, to_location=None, revision=None):
676
 
        from bzrlib.branch import copy_branch, find_cached_branch
677
 
        import tempfile
678
588
        import errno
 
589
        from bzrlib.merge import merge
 
590
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
591
             find_cached_branch, Branch
679
592
        from shutil import rmtree
 
593
        from meta_store import CachedStore
 
594
        import tempfile
680
595
        cache_root = tempfile.mkdtemp()
 
596
 
 
597
        if revision is None:
 
598
            revision = [None]
 
599
        elif len(revision) > 1:
 
600
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
 
601
 
681
602
        try:
682
 
            if revision is None:
683
 
                revision = [None]
684
 
            elif len(revision) > 1:
685
 
                raise BzrCommandError(
686
 
                    'bzr branch --revision takes exactly 1 revision value')
687
603
            try:
688
604
                br_from = find_cached_branch(from_location, cache_root)
689
605
            except OSError, e:
692
608
                                          ' exist.' % to_location)
693
609
                else:
694
610
                    raise
 
611
 
695
612
            if to_location is None:
696
613
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
614
 
697
615
            try:
698
616
                os.mkdir(to_location)
699
617
            except OSError, e:
705
623
                                          to_location)
706
624
                else:
707
625
                    raise
708
 
            try:
709
 
                copy_branch(br_from, to_location, revision[0])
710
 
            except bzrlib.errors.NoSuchRevision:
711
 
                rmtree(to_location)
712
 
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
713
 
                raise BzrCommandError(msg)
 
626
            br_to = Branch(to_location, init=True)
 
627
 
 
628
            br_to.set_root_id(br_from.get_root_id())
 
629
 
 
630
            if revision:
 
631
                if revision[0] is None:
 
632
                    revno = br_from.revno()
 
633
                else:
 
634
                    revno, rev_id = br_from.get_revision_info(revision[0])
 
635
                try:
 
636
                    br_to.update_revisions(br_from, stop_revision=revno)
 
637
                except NoSuchRevision:
 
638
                    rmtree(to_location)
 
639
                    msg = "The branch %s has no revision %d." % (from_location,
 
640
                                                                 revno)
 
641
                    raise BzrCommandError(msg)
 
642
            
 
643
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
644
                  check_clean=False, ignore_zero=True)
 
645
            from_location = pull_loc(br_from)
 
646
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
714
647
        finally:
715
648
            rmtree(cache_root)
716
649
 
717
650
 
 
651
def pull_loc(branch):
 
652
    # TODO: Should perhaps just make attribute be 'base' in
 
653
    # RemoteBranch and Branch?
 
654
    if hasattr(branch, "baseurl"):
 
655
        return branch.baseurl
 
656
    else:
 
657
        return branch.base
 
658
 
 
659
 
 
660
 
718
661
class cmd_renames(Command):
719
662
    """Show list of renamed files.
720
663
 
837
780
    If files are listed, only the changes in those files are listed.
838
781
    Otherwise, all changes for the tree are listed.
839
782
 
 
783
    TODO: Given two revision arguments, show the difference between them.
 
784
 
840
785
    TODO: Allow diff across branches.
841
786
 
842
787
    TODO: Option to use external diff command; could be GNU diff, wdiff,
851
796
          deleted files.
852
797
 
853
798
    TODO: This probably handles non-Unix newlines poorly.
854
 
 
855
 
    examples:
856
 
        bzr diff
857
 
        bzr diff -r1
858
 
        bzr diff -r1:2
859
799
    """
860
800
    
861
801
    takes_args = ['file*']
874
814
        else:
875
815
            b = find_branch('.')
876
816
 
 
817
        # TODO: Make show_diff support taking 2 arguments
 
818
        base_rev = None
877
819
        if revision is not None:
878
 
            if len(revision) == 1:
879
 
                show_diff(b, revision[0], specific_files=file_list,
880
 
                          external_diff_options=diff_options)
881
 
            elif len(revision) == 2:
882
 
                show_diff(b, revision[0], specific_files=file_list,
883
 
                          external_diff_options=diff_options,
884
 
                          revision2=revision[1])
885
 
            else:
886
 
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
887
 
        else:
888
 
            show_diff(b, None, specific_files=file_list,
889
 
                      external_diff_options=diff_options)
 
820
            if len(revision) != 1:
 
821
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
 
822
            base_rev = revision[0]
 
823
    
 
824
        show_diff(b, base_rev, specific_files=file_list,
 
825
                  external_diff_options=diff_options)
 
826
 
890
827
 
891
828
        
892
829
 
918
855
    """List files modified in working tree."""
919
856
    hidden = True
920
857
    def run(self):
921
 
        from bzrlib.delta import compare_trees
 
858
        from bzrlib.diff import compare_trees
922
859
 
923
860
        b = find_branch('.')
924
861
        td = compare_trees(b.basis_tree(), b.working_tree())
973
910
    """
974
911
 
975
912
    takes_args = ['filename?']
976
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
977
 
                     'long', 'message', 'short',]
 
913
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
978
914
    
979
915
    def run(self, filename=None, timezone='original',
980
916
            verbose=False,
982
918
            forward=False,
983
919
            revision=None,
984
920
            message=None,
985
 
            long=False,
986
 
            short=False):
 
921
            long=False):
987
922
        from bzrlib.branch import find_branch
988
923
        from bzrlib.log import log_formatter, show_log
989
924
        import codecs
1023
958
        # in e.g. the default C locale.
1024
959
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1025
960
 
1026
 
        if not short:
 
961
        if long:
1027
962
            log_format = 'long'
1028
963
        else:
1029
964
            log_format = 'short'
1246
1181
 
1247
1182
class cmd_commit(Command):
1248
1183
    """Commit changes into a new revision.
1249
 
    
1250
 
    If no arguments are given, the entire tree is committed.
1251
1184
 
1252
1185
    If selected files are specified, only changes to those files are
1253
 
    committed.  If a directory is specified then the directory and everything 
1254
 
    within it is committed.
 
1186
    committed.  If a directory is specified then its contents are also
 
1187
    committed.
1255
1188
 
1256
1189
    A selected-file commit may fail in some cases where the committed
1257
1190
    tree would be invalid, such as trying to commit a file in a
1265
1198
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1266
1199
    aliases = ['ci', 'checkin']
1267
1200
 
1268
 
    # TODO: Give better message for -s, --summary, used by tla people
1269
 
    
1270
1201
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1271
1202
            unchanged=False):
1272
1203
        from bzrlib.errors import PointlessCommit
1274
1205
 
1275
1206
        ## Warning: shadows builtin file()
1276
1207
        if not message and not file:
1277
 
            # FIXME: Ugly; change status code to send to a provided function?
1278
 
            
1279
1208
            import cStringIO
1280
1209
            stdout = sys.stdout
1281
1210
            catcher = cStringIO.StringIO()
1296
1225
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1297
1226
 
1298
1227
        b = find_branch('.')
1299
 
        if selected_list:
1300
 
            selected_list = [b.relpath(s) for s in selected_list]
1301
 
            
 
1228
 
1302
1229
        try:
1303
1230
            b.commit(message, verbose=verbose,
1304
1231
                     specific_files=selected_list,
1323
1250
 
1324
1251
    def run(self, dir='.'):
1325
1252
        from bzrlib.check import check
1326
 
 
1327
1253
        check(find_branch(dir))
1328
1254
 
1329
1255
 
1330
 
class cmd_scan_cache(Command):
1331
 
    hidden = True
1332
 
    def run(self):
1333
 
        from bzrlib.hashcache import HashCache
1334
 
        import os
1335
 
 
1336
 
        c = HashCache('.')
1337
 
        c.read()
1338
 
        c.scan()
1339
 
            
1340
 
        print '%6d stats' % c.stat_count
1341
 
        print '%6d in hashcache' % len(c._cache)
1342
 
        print '%6d files removed from cache' % c.removed_count
1343
 
        print '%6d hashes updated' % c.update_count
1344
 
        print '%6d files changed too recently to cache' % c.danger_count
1345
 
 
1346
 
        if c.needs_write:
1347
 
            c.write()
1348
 
            
1349
 
 
1350
1256
 
1351
1257
class cmd_upgrade(Command):
1352
1258
    """Upgrade branch storage to current format.
1367
1273
    takes_options = ['email']
1368
1274
    
1369
1275
    def run(self, email=False):
1370
 
        try:
1371
 
            b = bzrlib.branch.find_branch('.')
1372
 
        except:
1373
 
            b = None
1374
 
        
1375
1276
        if email:
1376
 
            print bzrlib.osutils.user_email(b)
 
1277
            print bzrlib.osutils.user_email()
1377
1278
        else:
1378
 
            print bzrlib.osutils.username(b)
 
1279
            print bzrlib.osutils.username()
1379
1280
 
1380
1281
 
1381
1282
class cmd_selftest(Command):
1382
1283
    """Run internal test suite"""
1383
1284
    hidden = True
1384
 
    takes_options = ['verbose', 'pattern']
1385
 
    def run(self, verbose=False, pattern=".*"):
1386
 
        import bzrlib.ui
 
1285
    def run(self):
1387
1286
        from bzrlib.selftest import selftest
1388
 
        # we don't want progress meters from the tests to go to the
1389
 
        # real output; and we don't want log messages cluttering up
1390
 
        # the real logs.
1391
 
        save_ui = bzrlib.ui.ui_factory
1392
 
        bzrlib.trace.info('running tests...')
1393
 
        bzrlib.trace.disable_default_logging()
1394
 
        try:
1395
 
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1396
 
            result = selftest(verbose=verbose, pattern=pattern)
1397
 
            if result:
1398
 
                bzrlib.trace.info('tests passed')
1399
 
            else:
1400
 
                bzrlib.trace.info('tests failed')
1401
 
            return int(not result)
1402
 
        finally:
1403
 
            bzrlib.trace.enable_default_logging()
1404
 
            bzrlib.ui.ui_factory = save_ui
 
1287
        return int(not selftest())
1405
1288
 
1406
1289
 
1407
1290
class cmd_version(Command):
1462
1345
 
1463
1346
 
1464
1347
 
1465
 
class cmd_find_merge_base(Command):
1466
 
    """Find and print a base revision for merging two branches.
1467
 
 
1468
 
    TODO: Options to specify revisions on either side, as if
1469
 
          merging only part of the history.
1470
 
    """
1471
 
    takes_args = ['branch', 'other']
1472
 
    hidden = True
1473
 
    
1474
 
    def run(self, branch, other):
1475
 
        branch1 = find_branch(branch)
1476
 
        branch2 = find_branch(other)
1477
 
 
1478
 
        base_revno, base_revid = branch1.common_ancestor(branch2)
1479
 
 
1480
 
        if base_revno is None:
1481
 
            raise bzrlib.errors.UnrelatedBranches()
1482
 
 
1483
 
        print 'merge base is revision %s' % base_revid
1484
 
        print ' r%-6d in %s' % (base_revno, branch)
1485
 
 
1486
 
        other_revno = branch2.revision_id_to_revno(base_revid)
1487
 
        
1488
 
        print ' r%-6d in %s' % (other_revno, other)
1489
 
 
1490
 
 
1491
 
 
1492
1348
class cmd_merge(Command):
1493
 
    """Perform a three-way merge.
1494
 
    
1495
 
    The branch is the branch you will merge from.  By default, it will merge
1496
 
    the latest revision.  If you specify a revision, that revision will be
1497
 
    merged.  If you specify two revisions, the first will be used as a BASE, 
1498
 
    and the second one as OTHER.  Revision numbers are always relative to the
1499
 
    specified branch.
1500
 
    
1501
 
    Examples:
1502
 
 
1503
 
    To merge the latest revision from bzr.dev
1504
 
    bzr merge ../bzr.dev
1505
 
 
1506
 
    To merge changes up to and including revision 82 from bzr.dev
1507
 
    bzr merge -r 82 ../bzr.dev
1508
 
 
1509
 
    To merge the changes introduced by 82, without previous changes:
1510
 
    bzr merge -r 81..82 ../bzr.dev
1511
 
    
 
1349
    """Perform a three-way merge of trees.
 
1350
    
 
1351
    The SPEC parameters are working tree or revision specifiers.  Working trees
 
1352
    are specified using standard paths or urls.  No component of a directory
 
1353
    path may begin with '@'.
 
1354
    
 
1355
    Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
 
1356
 
 
1357
    Revisions are specified using a dirname/@revno pair, where dirname is the
 
1358
    branch directory and revno is the revision within that branch.  If no revno
 
1359
    is specified, the latest revision is used.
 
1360
 
 
1361
    Revision examples: './@127', 'foo/@', '../@1'
 
1362
 
 
1363
    The OTHER_SPEC parameter is required.  If the BASE_SPEC parameter is
 
1364
    not supplied, the common ancestor of OTHER_SPEC the current branch is used
 
1365
    as the BASE.
 
1366
 
1512
1367
    merge refuses to run if there are any uncommitted changes, unless
1513
1368
    --force is given.
1514
1369
    """
1515
 
    takes_args = ['branch?']
1516
 
    takes_options = ['revision', 'force', 'merge-type']
 
1370
    takes_args = ['other_spec', 'base_spec?']
 
1371
    takes_options = ['force']
1517
1372
 
1518
 
    def run(self, branch='.', revision=None, force=False, 
1519
 
            merge_type=None):
 
1373
    def run(self, other_spec, base_spec=None, force=False):
1520
1374
        from bzrlib.merge import merge
1521
 
        from bzrlib.merge_core import ApplyMerge3
1522
 
        if merge_type is None:
1523
 
            merge_type = ApplyMerge3
 
1375
        merge(parse_spec(other_spec), parse_spec(base_spec),
 
1376
              check_clean=(not force))
1524
1377
 
1525
 
        if revision is None or len(revision) < 1:
1526
 
            base = [None, None]
1527
 
            other = [branch, -1]
1528
 
        else:
1529
 
            if len(revision) == 1:
1530
 
                other = [branch, revision[0]]
1531
 
                base = [None, None]
1532
 
            else:
1533
 
                assert len(revision) == 2
1534
 
                if None in revision:
1535
 
                    raise BzrCommandError(
1536
 
                        "Merge doesn't permit that revision specifier.")
1537
 
                base = (branch, revision[0])
1538
 
                other = (branch, revision[1])
1539
 
            
1540
 
        merge(other, base, check_clean=(not force), merge_type=merge_type)
1541
1378
 
1542
1379
 
1543
1380
class cmd_revert(Command):
 
1381
    """Restore selected files from a previous revision.
 
1382
    """
 
1383
    takes_args = ['file+']
 
1384
    def run(self, file_list):
 
1385
        from bzrlib.branch import find_branch
 
1386
        
 
1387
        if not file_list:
 
1388
            file_list = ['.']
 
1389
            
 
1390
        b = find_branch(file_list[0])
 
1391
 
 
1392
        b.revert([b.relpath(f) for f in file_list])
 
1393
 
 
1394
 
 
1395
class cmd_merge_revert(Command):
1544
1396
    """Reverse all changes since the last commit.
1545
1397
 
1546
 
    Only versioned files are affected.  Specify filenames to revert only 
1547
 
    those files.  By default, any files that are changed will be backed up
1548
 
    first.  Backup files have a '~' appended to their name.
 
1398
    Only versioned files are affected.
 
1399
 
 
1400
    TODO: Store backups of any files that will be reverted, so
 
1401
          that the revert can be undone.          
1549
1402
    """
1550
 
    takes_options = ['revision', 'no-backup']
1551
 
    takes_args = ['file*']
1552
 
    aliases = ['merge-revert']
 
1403
    takes_options = ['revision']
1553
1404
 
1554
 
    def run(self, revision=None, no_backup=False, file_list=None):
 
1405
    def run(self, revision=None):
1555
1406
        from bzrlib.merge import merge
1556
 
        from bzrlib.branch import Branch
1557
 
        if file_list is not None:
1558
 
            if len(file_list) == 0:
1559
 
                raise BzrCommandError("No files specified")
1560
1407
        if revision is None:
1561
1408
            revision = [-1]
1562
1409
        elif len(revision) != 1:
1563
 
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
 
1410
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1564
1411
        merge(('.', revision[0]), parse_spec('.'),
1565
1412
              check_clean=False,
1566
 
              ignore_zero=True,
1567
 
              backup_files=not no_backup,
1568
 
              file_list=file_list)
1569
 
        if not file_list:
1570
 
            Branch('.').set_pending_merges([])
 
1413
              ignore_zero=True)
1571
1414
 
1572
1415
 
1573
1416
class cmd_assert_fail(Command):
1581
1424
    """Show help on a command or other topic.
1582
1425
 
1583
1426
    For a list of all available commands, say 'bzr help commands'."""
1584
 
    takes_options = ['long']
1585
1427
    takes_args = ['topic?']
1586
1428
    aliases = ['?']
1587
1429
    
1588
 
    def run(self, topic=None, long=False):
 
1430
    def run(self, topic=None):
1589
1431
        import help
1590
 
        if topic is None and long:
1591
 
            topic = "commands"
1592
1432
        help.help(topic)
1593
1433
 
1594
1434
 
1595
 
class cmd_shell_complete(Command):
1596
 
    """Show appropriate completions for context.
1597
 
 
1598
 
    For a list of all available commands, say 'bzr shell-complete'."""
1599
 
    takes_args = ['context?']
1600
 
    aliases = ['s-c']
1601
 
    hidden = True
1602
 
    
1603
 
    def run(self, context=None):
1604
 
        import shellcomplete
1605
 
        shellcomplete.shellcomplete(context)
1606
 
 
1607
 
 
1608
 
class cmd_missing(Command):
1609
 
    """What is missing in this branch relative to other branch.
1610
 
    """
1611
 
    takes_args = ['remote?']
1612
 
    aliases = ['mis', 'miss']
1613
 
    # We don't have to add quiet to the list, because 
1614
 
    # unknown options are parsed as booleans
1615
 
    takes_options = ['verbose', 'quiet']
1616
 
 
1617
 
    def run(self, remote=None, verbose=False, quiet=False):
1618
 
        from bzrlib.branch import find_branch, DivergedBranches
1619
 
        from bzrlib.errors import BzrCommandError
1620
 
        from bzrlib.missing import get_parent, show_missing
1621
 
 
1622
 
        if verbose and quiet:
1623
 
            raise BzrCommandError('Cannot pass both quiet and verbose')
1624
 
 
1625
 
        b = find_branch('.')
1626
 
        parent = get_parent(b)
1627
 
        if remote is None:
1628
 
            if parent is None:
1629
 
                raise BzrCommandError("No missing location known or specified.")
1630
 
            else:
1631
 
                if not quiet:
1632
 
                    print "Using last location: %s" % parent
1633
 
                remote = parent
1634
 
        elif parent is None:
1635
 
            # We only update x-pull if it did not exist, missing should not change the parent
1636
 
            b.controlfile('x-pull', 'wb').write(remote + '\n')
1637
 
        br_remote = find_branch(remote)
1638
 
 
1639
 
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1640
 
 
1641
1435
 
1642
1436
 
1643
1437
class cmd_plugins(Command):
1672
1466
    'no-recurse':             None,
1673
1467
    'profile':                None,
1674
1468
    'revision':               _parse_revision_str,
1675
 
    'short':                  None,
1676
1469
    'show-ids':               None,
1677
1470
    'timezone':               str,
1678
1471
    'verbose':                None,
1682
1475
    'update':                 None,
1683
1476
    'long':                   None,
1684
1477
    'root':                   str,
1685
 
    'no-backup':              None,
1686
 
    'merge-type':             get_merge_type,
1687
 
    'pattern':                str,
1688
1478
    }
1689
1479
 
1690
1480
SHORT_OPTIONS = {
1840
1630
    return argdict
1841
1631
 
1842
1632
 
 
1633
def _parse_master_args(argv):
 
1634
    """Parse the arguments that always go with the original command.
 
1635
    These are things like bzr --no-plugins, etc.
 
1636
 
 
1637
    There are now 2 types of option flags. Ones that come *before* the command,
 
1638
    and ones that come *after* the command.
 
1639
    Ones coming *before* the command are applied against all possible commands.
 
1640
    And are generally applied before plugins are loaded.
 
1641
 
 
1642
    The current list are:
 
1643
        --builtin   Allow plugins to load, but don't let them override builtin commands,
 
1644
                    they will still be allowed if they do not override a builtin.
 
1645
        --no-plugins    Don't load any plugins. This lets you get back to official source
 
1646
                        behavior.
 
1647
        --profile   Enable the hotspot profile before running the command.
 
1648
                    For backwards compatibility, this is also a non-master option.
 
1649
        --version   Spit out the version of bzr that is running and exit.
 
1650
                    This is also a non-master option.
 
1651
        --help      Run help and exit, also a non-master option (I think that should stay, though)
 
1652
 
 
1653
    >>> argv, opts = _parse_master_args(['bzr', '--test'])
 
1654
    Traceback (most recent call last):
 
1655
    ...
 
1656
    BzrCommandError: Invalid master option: 'test'
 
1657
    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
 
1658
    >>> print argv
 
1659
    ['command']
 
1660
    >>> print opts['version']
 
1661
    True
 
1662
    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
 
1663
    >>> print argv
 
1664
    ['command', '--more-options']
 
1665
    >>> print opts['profile']
 
1666
    True
 
1667
    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
 
1668
    >>> print argv
 
1669
    ['command']
 
1670
    >>> print opts['no-plugins']
 
1671
    True
 
1672
    >>> print opts['profile']
 
1673
    False
 
1674
    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
 
1675
    >>> print argv
 
1676
    ['command', '--profile']
 
1677
    >>> print opts['profile']
 
1678
    False
 
1679
    """
 
1680
    master_opts = {'builtin':False,
 
1681
        'no-plugins':False,
 
1682
        'version':False,
 
1683
        'profile':False,
 
1684
        'help':False
 
1685
    }
 
1686
 
 
1687
    # This is the point where we could hook into argv[0] to determine
 
1688
    # what front-end is supposed to be run
 
1689
    # For now, we are just ignoring it.
 
1690
    cmd_name = argv.pop(0)
 
1691
    for arg in argv[:]:
 
1692
        if arg[:2] != '--': # at the first non-option, we return the rest
 
1693
            break
 
1694
        arg = arg[2:] # Remove '--'
 
1695
        if arg not in master_opts:
 
1696
            # We could say that this is not an error, that we should
 
1697
            # just let it be handled by the main section instead
 
1698
            raise BzrCommandError('Invalid master option: %r' % arg)
 
1699
        argv.pop(0) # We are consuming this entry
 
1700
        master_opts[arg] = True
 
1701
    return argv, master_opts
 
1702
 
 
1703
 
1843
1704
 
1844
1705
def run_bzr(argv):
1845
1706
    """Execute a command.
1846
1707
 
1847
1708
    This is similar to main(), but without all the trappings for
1848
1709
    logging and error handling.  
1849
 
    
1850
 
    argv
1851
 
       The command-line arguments, without the program name from argv[0]
1852
 
    
1853
 
    Returns a command status or raises an exception.
1854
 
 
1855
 
    Special master options: these must come before the command because
1856
 
    they control how the command is interpreted.
1857
 
 
1858
 
    --no-plugins
1859
 
        Do not load plugin modules at all
1860
 
 
1861
 
    --builtin
1862
 
        Only use builtin commands.  (Plugins are still allowed to change
1863
 
        other behaviour.)
1864
 
 
1865
 
    --profile
1866
 
        Run under the Python profiler.
1867
1710
    """
1868
 
    
1869
1711
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1870
 
 
1871
 
    opt_profile = opt_no_plugins = opt_builtin = False
1872
 
 
1873
 
    # --no-plugins is handled specially at a very early stage. We need
1874
 
    # to load plugins before doing other command parsing so that they
1875
 
    # can override commands, but this needs to happen first.
1876
 
 
1877
 
    for a in argv[:]:
1878
 
        if a == '--profile':
1879
 
            opt_profile = True
1880
 
        elif a == '--no-plugins':
1881
 
            opt_no_plugins = True
1882
 
        elif a == '--builtin':
1883
 
            opt_builtin = True
1884
 
        else:
1885
 
            break
1886
 
        argv.remove(a)
1887
 
 
1888
 
    if not opt_no_plugins:
1889
 
        from bzrlib.plugin import load_plugins
1890
 
        load_plugins()
1891
 
 
1892
 
    args, opts = parse_args(argv)
1893
 
 
1894
 
    if 'help' in opts:
1895
 
        from bzrlib.help import help
1896
 
        if args:
1897
 
            help(args[0])
1898
 
        else:
1899
 
            help()
1900
 
        return 0            
1901
 
        
1902
 
    if 'version' in opts:
1903
 
        show_version()
1904
 
        return 0
1905
 
    
1906
 
    if not args:
1907
 
        from bzrlib.help import help
1908
 
        help(None)
1909
 
        return 0
1910
 
    
1911
 
    cmd = str(args.pop(0))
1912
 
 
1913
 
    canonical_cmd, cmd_class = \
1914
 
                   get_cmd_class(cmd, plugins_override=not opt_builtin)
 
1712
    
 
1713
    try:
 
1714
        # some options like --builtin and --no-plugins have special effects
 
1715
        argv, master_opts = _parse_master_args(argv)
 
1716
        if not master_opts['no-plugins']:
 
1717
            from bzrlib.plugin import load_plugins
 
1718
            load_plugins()
 
1719
 
 
1720
        args, opts = parse_args(argv)
 
1721
 
 
1722
        if master_opts['help']:
 
1723
            from bzrlib.help import help
 
1724
            if argv:
 
1725
                help(argv[0])
 
1726
            else:
 
1727
                help()
 
1728
            return 0            
 
1729
            
 
1730
        if 'help' in opts:
 
1731
            from bzrlib.help import help
 
1732
            if args:
 
1733
                help(args[0])
 
1734
            else:
 
1735
                help()
 
1736
            return 0
 
1737
        elif 'version' in opts:
 
1738
            show_version()
 
1739
            return 0
 
1740
        elif args and args[0] == 'builtin':
 
1741
            include_plugins=False
 
1742
            args = args[1:]
 
1743
        cmd = str(args.pop(0))
 
1744
    except IndexError:
 
1745
        import help
 
1746
        help.help()
 
1747
        return 1
 
1748
          
 
1749
 
 
1750
    plugins_override = not (master_opts['builtin'])
 
1751
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
 
1752
 
 
1753
    profile = master_opts['profile']
 
1754
    # For backwards compatibility, I would rather stick with --profile being a
 
1755
    # master/global option
 
1756
    if 'profile' in opts:
 
1757
        profile = True
 
1758
        del opts['profile']
1915
1759
 
1916
1760
    # check options are reasonable
1917
1761
    allowed = cmd_class.takes_options
1926
1770
    for k, v in opts.items():
1927
1771
        cmdopts[k.replace('-', '_')] = v
1928
1772
 
1929
 
    if opt_profile:
 
1773
    if profile:
1930
1774
        import hotshot, tempfile
1931
1775
        pffileno, pfname = tempfile.mkstemp()
1932
1776
        try:
1951
1795
        return cmd_class(cmdopts, cmdargs).status 
1952
1796
 
1953
1797
 
 
1798
def _report_exception(summary, quiet=False):
 
1799
    import traceback
 
1800
    log_error('bzr: ' + summary)
 
1801
    bzrlib.trace.log_exception()
 
1802
 
 
1803
    if not quiet:
 
1804
        tb = sys.exc_info()[2]
 
1805
        exinfo = traceback.extract_tb(tb)
 
1806
        if exinfo:
 
1807
            sys.stderr.write('  at %s:%d in %s()\n' % exinfo[-1][:3])
 
1808
        sys.stderr.write('  see ~/.bzr.log for debug information\n')
 
1809
 
 
1810
 
 
1811
 
1954
1812
def main(argv):
1955
 
    import bzrlib.ui
1956
 
    bzrlib.trace.log_startup(argv)
1957
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
1813
    
 
1814
    bzrlib.trace.open_tracefile(argv)
1958
1815
 
1959
1816
    try:
1960
1817
        try:
1961
 
            return run_bzr(argv[1:])
1962
 
        finally:
1963
 
            # do this here inside the exception wrappers to catch EPIPE
1964
 
            sys.stdout.flush()
1965
 
    except BzrCommandError, e:
1966
 
        # command line syntax error, etc
1967
 
        log_error(str(e))
1968
 
        return 1
1969
 
    except BzrError, e:
1970
 
        bzrlib.trace.log_exception()
1971
 
        return 1
1972
 
    except AssertionError, e:
1973
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
1974
 
        return 3
1975
 
    except KeyboardInterrupt, e:
1976
 
        bzrlib.trace.note('interrupted')
1977
 
        return 2
1978
 
    except Exception, e:
1979
 
        import errno
1980
 
        if (isinstance(e, IOError) 
1981
 
            and hasattr(e, 'errno')
1982
 
            and e.errno == errno.EPIPE):
1983
 
            bzrlib.trace.note('broken pipe')
1984
 
            return 2
1985
 
        else:
1986
 
            bzrlib.trace.log_exception()
1987
 
            return 2
 
1818
            try:
 
1819
                return run_bzr(argv)
 
1820
            finally:
 
1821
                # do this here inside the exception wrappers to catch EPIPE
 
1822
                sys.stdout.flush()
 
1823
        except BzrError, e:
 
1824
            quiet = isinstance(e, (BzrCommandError))
 
1825
            _report_exception('error: ' + e.args[0], quiet=quiet)
 
1826
            if len(e.args) > 1:
 
1827
                for h in e.args[1]:
 
1828
                    # some explanation or hints
 
1829
                    log_error('  ' + h)
 
1830
            return 1
 
1831
        except AssertionError, e:
 
1832
            msg = 'assertion failed'
 
1833
            if str(e):
 
1834
                msg += ': ' + str(e)
 
1835
            _report_exception(msg)
 
1836
            return 2
 
1837
        except KeyboardInterrupt, e:
 
1838
            _report_exception('interrupted', quiet=True)
 
1839
            return 2
 
1840
        except Exception, e:
 
1841
            import errno
 
1842
            quiet = False
 
1843
            if (isinstance(e, IOError) 
 
1844
                and hasattr(e, 'errno')
 
1845
                and e.errno == errno.EPIPE):
 
1846
                quiet = True
 
1847
                msg = 'broken pipe'
 
1848
            else:
 
1849
                msg = str(e).rstrip('\n')
 
1850
            _report_exception(msg, quiet)
 
1851
            return 2
 
1852
    finally:
 
1853
        bzrlib.trace.close_trace()
1988
1854
 
1989
1855
 
1990
1856
if __name__ == '__main__':