~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-07-04 11:57:18 UTC
  • Revision ID: mbp@sourcefrog.net-20050704115718-b532986c0714e7a7
- don't write precursor field in new revision xml
- make parents more primary; remove more precursor code
- test commit of revision with parents

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
import bzrlib
22
22
from bzrlib.trace import mutter, note, log_error
23
 
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
24
 
from bzrlib.osutils import quotefn
25
 
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
26
 
     format_date
 
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
 
24
from bzrlib.branch import find_branch
 
25
from bzrlib import BZRDIR
 
26
 
 
27
 
 
28
plugin_cmds = {}
 
29
 
 
30
 
 
31
def register_command(cmd):
 
32
    "Utility function to help register a command"
 
33
    global plugin_cmds
 
34
    k = cmd.__name__
 
35
    if k.startswith("cmd_"):
 
36
        k_unsquished = _unsquish_command_name(k)
 
37
    else:
 
38
        k_unsquished = k
 
39
    if not plugin_cmds.has_key(k_unsquished):
 
40
        plugin_cmds[k_unsquished] = cmd
 
41
    else:
 
42
        log_error('Two plugins defined the same command: %r' % k)
 
43
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
27
44
 
28
45
 
29
46
def _squish_command_name(cmd):
68
85
        revs = int(revstr)
69
86
    return revs
70
87
 
71
 
def get_all_cmds():
72
 
    """Return canonical name and class for all registered commands."""
 
88
 
 
89
 
 
90
def _get_cmd_dict(plugins_override=True):
 
91
    d = {}
73
92
    for k, v in globals().iteritems():
74
93
        if k.startswith("cmd_"):
75
 
            yield _unsquish_command_name(k), v
76
 
 
77
 
def get_cmd_class(cmd):
 
94
            d[_unsquish_command_name(k)] = v
 
95
    # If we didn't load plugins, the plugin_cmds dict will be empty
 
96
    if plugins_override:
 
97
        d.update(plugin_cmds)
 
98
    else:
 
99
        d2 = plugin_cmds.copy()
 
100
        d2.update(d)
 
101
        d = d2
 
102
    return d
 
103
 
 
104
    
 
105
def get_all_cmds(plugins_override=True):
 
106
    """Return canonical name and class for all registered commands."""
 
107
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
108
        yield k,v
 
109
 
 
110
 
 
111
def get_cmd_class(cmd, plugins_override=True):
78
112
    """Return the canonical name and command class for a command.
79
113
    """
80
114
    cmd = str(cmd)                      # not unicode
81
115
 
82
116
    # first look up this command under the specified name
 
117
    cmds = _get_cmd_dict(plugins_override=plugins_override)
83
118
    try:
84
 
        return cmd, globals()[_squish_command_name(cmd)]
 
119
        return cmd, cmds[cmd]
85
120
    except KeyError:
86
121
        pass
87
122
 
88
123
    # look for any command which claims this as an alias
89
 
    for cmdname, cmdclass in get_all_cmds():
 
124
    for cmdname, cmdclass in cmds.iteritems():
90
125
        if cmd in cmdclass.aliases:
91
126
            return cmdname, cmdclass
92
127
 
165
200
        import os.path
166
201
        bzrpath = os.environ.get('BZRPATH', '')
167
202
 
168
 
        for dir in bzrpath.split(':'):
 
203
        for dir in bzrpath.split(os.pathsep):
169
204
            path = os.path.join(dir, cmd)
170
205
            if os.path.isfile(path):
171
206
                return ExternalCommand(path)
177
212
    def __init__(self, path):
178
213
        self.path = path
179
214
 
180
 
        # TODO: If either of these fail, we should detect that and
181
 
        # assume that path is not really a bzr plugin after all.
182
 
 
183
215
        pipe = os.popen('%s --bzr-usage' % path, 'r')
184
216
        self.takes_options = pipe.readline().split()
 
217
 
 
218
        for opt in self.takes_options:
 
219
            if not opt in OPTIONS:
 
220
                raise BzrError("Unknown option '%s' returned by external command %s"
 
221
                               % (opt, path))
 
222
 
 
223
        # TODO: Is there any way to check takes_args is valid here?
185
224
        self.takes_args = pipe.readline().split()
186
 
        pipe.close()
 
225
 
 
226
        if pipe.close() is not None:
 
227
            raise BzrError("Failed funning '%s --bzr-usage'" % path)
187
228
 
188
229
        pipe = os.popen('%s --bzr-help' % path, 'r')
189
230
        self.__doc__ = pipe.read()
190
 
        pipe.close()
 
231
        if pipe.close() is not None:
 
232
            raise BzrError("Failed funning '%s --bzr-help'" % path)
191
233
 
192
234
    def __call__(self, options, arguments):
193
235
        Command.__init__(self, options, arguments)
200
242
        keys = kargs.keys()
201
243
        keys.sort()
202
244
        for name in keys:
 
245
            optname = name.replace('_','-')
203
246
            value = kargs[name]
204
 
            if OPTIONS.has_key(name):
 
247
            if OPTIONS.has_key(optname):
205
248
                # it's an option
206
 
                opts.append('--%s' % name)
 
249
                opts.append('--%s' % optname)
207
250
                if value is not None and value is not True:
208
251
                    opts.append(str(value))
209
252
            else:
260
303
    
261
304
    def run(self, all=False, show_ids=False, file_list=None):
262
305
        if file_list:
263
 
            b = Branch(file_list[0])
 
306
            b = find_branch(file_list[0])
264
307
            file_list = [b.relpath(x) for x in file_list]
265
308
            # special case: only one path was given and it's the root
266
309
            # of the branch
267
310
            if file_list == ['']:
268
311
                file_list = None
269
312
        else:
270
 
            b = Branch('.')
 
313
            b = find_branch('.')
271
314
        import status
272
315
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
273
316
                           specific_files=file_list)
280
323
    takes_args = ['revision_id']
281
324
    
282
325
    def run(self, revision_id):
283
 
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
 
326
        from bzrlib.xml import pack_xml
 
327
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
284
328
 
285
329
 
286
330
class cmd_revno(Command):
288
332
 
289
333
    This is equal to the number of revisions on this branch."""
290
334
    def run(self):
291
 
        print Branch('.').revno()
 
335
        print find_branch('.').revno()
292
336
 
293
337
    
294
338
class cmd_add(Command):
316
360
    takes_options = ['verbose', 'no-recurse']
317
361
    
318
362
    def run(self, file_list, verbose=False, no_recurse=False):
319
 
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
 
363
        from bzrlib.add import smart_add
 
364
        smart_add(file_list, verbose, not no_recurse)
 
365
 
 
366
 
 
367
 
 
368
class cmd_mkdir(Command):
 
369
    """Create a new versioned directory.
 
370
 
 
371
    This is equivalent to creating the directory and then adding it.
 
372
    """
 
373
    takes_args = ['dir+']
 
374
 
 
375
    def run(self, dir_list):
 
376
        b = None
 
377
        
 
378
        for d in dir_list:
 
379
            os.mkdir(d)
 
380
            if not b:
 
381
                b = find_branch(d)
 
382
            b.add([d], verbose=True)
320
383
 
321
384
 
322
385
class cmd_relpath(Command):
325
388
    hidden = True
326
389
    
327
390
    def run(self, filename):
328
 
        print Branch(filename).relpath(filename)
 
391
        print find_branch(filename).relpath(filename)
329
392
 
330
393
 
331
394
 
334
397
    takes_options = ['revision', 'show-ids']
335
398
    
336
399
    def run(self, revision=None, show_ids=False):
337
 
        b = Branch('.')
 
400
        b = find_branch('.')
338
401
        if revision == None:
339
402
            inv = b.read_working_inventory()
340
403
        else:
357
420
    """
358
421
    takes_args = ['source$', 'dest']
359
422
    def run(self, source_list, dest):
360
 
        b = Branch('.')
 
423
        b = find_branch('.')
361
424
 
362
425
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
363
426
 
379
442
    takes_args = ['from_name', 'to_name']
380
443
    
381
444
    def run(self, from_name, to_name):
382
 
        b = Branch('.')
 
445
        b = find_branch('.')
383
446
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
384
447
 
385
448
 
386
449
 
 
450
 
 
451
 
 
452
class cmd_pull(Command):
 
453
    """Pull any changes from another branch into the current one.
 
454
 
 
455
    If the location is omitted, the last-used location will be used.
 
456
    Both the revision history and the working directory will be
 
457
    updated.
 
458
 
 
459
    This command only works on branches that have not diverged.  Branches are
 
460
    considered diverged if both branches have had commits without first
 
461
    pulling from the other.
 
462
 
 
463
    If branches have diverged, you can use 'bzr merge' to pull the text changes
 
464
    from one into the other.
 
465
    """
 
466
    takes_args = ['location?']
 
467
 
 
468
    def run(self, location=None):
 
469
        from bzrlib.merge import merge
 
470
        import tempfile
 
471
        from shutil import rmtree
 
472
        import errno
 
473
        
 
474
        br_to = find_branch('.')
 
475
        stored_loc = None
 
476
        try:
 
477
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
 
478
        except IOError, e:
 
479
            if e.errno != errno.ENOENT:
 
480
                raise
 
481
        if location is None:
 
482
            if stored_loc is None:
 
483
                raise BzrCommandError("No pull location known or specified.")
 
484
            else:
 
485
                print "Using last location: %s" % stored_loc
 
486
                location = stored_loc
 
487
        cache_root = tempfile.mkdtemp()
 
488
        from bzrlib.branch import DivergedBranches
 
489
        br_from = find_branch(location)
 
490
        location = pull_loc(br_from)
 
491
        old_revno = br_to.revno()
 
492
        try:
 
493
            from branch import find_cached_branch, DivergedBranches
 
494
            br_from = find_cached_branch(location, cache_root)
 
495
            location = pull_loc(br_from)
 
496
            old_revno = br_to.revno()
 
497
            try:
 
498
                br_to.update_revisions(br_from)
 
499
            except DivergedBranches:
 
500
                raise BzrCommandError("These branches have diverged."
 
501
                    "  Try merge.")
 
502
                
 
503
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
504
            if location != stored_loc:
 
505
                br_to.controlfile("x-pull", "wb").write(location + "\n")
 
506
        finally:
 
507
            rmtree(cache_root)
 
508
 
 
509
 
 
510
 
 
511
class cmd_branch(Command):
 
512
    """Create a new copy of a branch.
 
513
 
 
514
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
 
515
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
 
516
 
 
517
    To retrieve the branch as of a particular revision, supply the --revision
 
518
    parameter, as in "branch foo/bar -r 5".
 
519
    """
 
520
    takes_args = ['from_location', 'to_location?']
 
521
    takes_options = ['revision']
 
522
 
 
523
    def run(self, from_location, to_location=None, revision=None):
 
524
        import errno
 
525
        from bzrlib.merge import merge
 
526
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
527
             find_cached_branch, Branch
 
528
        from shutil import rmtree
 
529
        from meta_store import CachedStore
 
530
        import tempfile
 
531
        cache_root = tempfile.mkdtemp()
 
532
        try:
 
533
            try:
 
534
                br_from = find_cached_branch(from_location, cache_root)
 
535
            except OSError, e:
 
536
                if e.errno == errno.ENOENT:
 
537
                    raise BzrCommandError('Source location "%s" does not'
 
538
                                          ' exist.' % to_location)
 
539
                else:
 
540
                    raise
 
541
 
 
542
            if to_location is None:
 
543
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
544
 
 
545
            try:
 
546
                os.mkdir(to_location)
 
547
            except OSError, e:
 
548
                if e.errno == errno.EEXIST:
 
549
                    raise BzrCommandError('Target directory "%s" already'
 
550
                                          ' exists.' % to_location)
 
551
                if e.errno == errno.ENOENT:
 
552
                    raise BzrCommandError('Parent of "%s" does not exist.' %
 
553
                                          to_location)
 
554
                else:
 
555
                    raise
 
556
            br_to = Branch(to_location, init=True)
 
557
 
 
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)
 
565
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
566
                  check_clean=False, ignore_zero=True)
 
567
            from_location = pull_loc(br_from)
 
568
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
569
        finally:
 
570
            rmtree(cache_root)
 
571
 
 
572
 
 
573
def pull_loc(branch):
 
574
    # TODO: Should perhaps just make attribute be 'base' in
 
575
    # RemoteBranch and Branch?
 
576
    if hasattr(branch, "baseurl"):
 
577
        return branch.baseurl
 
578
    else:
 
579
        return branch.base
 
580
 
 
581
 
 
582
 
387
583
class cmd_renames(Command):
388
584
    """Show list of renamed files.
389
585
 
394
590
    takes_args = ['dir?']
395
591
 
396
592
    def run(self, dir='.'):
397
 
        b = Branch(dir)
 
593
        b = find_branch(dir)
398
594
        old_inv = b.basis_tree().inventory
399
595
        new_inv = b.read_working_inventory()
400
596
 
411
607
    def run(self, branch=None):
412
608
        import info
413
609
 
414
 
        from branch import find_branch
415
610
        b = find_branch(branch)
416
611
        info.show_info(b)
417
612
 
426
621
    takes_options = ['verbose']
427
622
    
428
623
    def run(self, file_list, verbose=False):
429
 
        b = Branch(file_list[0])
 
624
        b = find_branch(file_list[0])
430
625
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
431
626
 
432
627
 
440
635
    hidden = True
441
636
    takes_args = ['filename']
442
637
    def run(self, filename):
443
 
        b = Branch(filename)
 
638
        b = find_branch(filename)
444
639
        i = b.inventory.path2id(b.relpath(filename))
445
640
        if i == None:
446
 
            bailout("%r is not a versioned file" % filename)
 
641
            raise BzrError("%r is not a versioned file" % filename)
447
642
        else:
448
643
            print i
449
644
 
456
651
    hidden = True
457
652
    takes_args = ['filename']
458
653
    def run(self, filename):
459
 
        b = Branch(filename)
 
654
        b = find_branch(filename)
460
655
        inv = b.inventory
461
656
        fid = inv.path2id(b.relpath(filename))
462
657
        if fid == None:
463
 
            bailout("%r is not a versioned file" % filename)
 
658
            raise BzrError("%r is not a versioned file" % filename)
464
659
        for fip in inv.get_idpath(fid):
465
660
            print fip
466
661
 
469
664
    """Display list of revision ids on this branch."""
470
665
    hidden = True
471
666
    def run(self):
472
 
        for patchid in Branch('.').revision_history():
 
667
        for patchid in find_branch('.').revision_history():
473
668
            print patchid
474
669
 
475
670
 
476
671
class cmd_directories(Command):
477
672
    """Display list of versioned directories in this branch."""
478
673
    def run(self):
479
 
        for name, ie in Branch('.').read_working_inventory().directories():
 
674
        for name, ie in find_branch('.').read_working_inventory().directories():
480
675
            if name == '':
481
676
                print '.'
482
677
            else:
497
692
        bzr commit -m 'imported project'
498
693
    """
499
694
    def run(self):
 
695
        from bzrlib.branch import Branch
500
696
        Branch('.', init=True)
501
697
 
502
698
 
526
722
    
527
723
    takes_args = ['file*']
528
724
    takes_options = ['revision', 'diff-options']
529
 
    aliases = ['di']
 
725
    aliases = ['di', 'dif']
530
726
 
531
727
    def run(self, revision=None, file_list=None, diff_options=None):
532
728
        from bzrlib.diff import show_diff
533
 
        from bzrlib import find_branch
534
729
 
535
730
        if file_list:
536
731
            b = find_branch(file_list[0])
539
734
                # just pointing to top-of-tree
540
735
                file_list = None
541
736
        else:
542
 
            b = Branch('.')
 
737
            b = find_branch('.')
543
738
    
544
739
        show_diff(b, revision, specific_files=file_list,
545
740
                  external_diff_options=diff_options)
554
749
    TODO: Show files deleted since a previous revision, or between two revisions.
555
750
    """
556
751
    def run(self, show_ids=False):
557
 
        b = Branch('.')
 
752
        b = find_branch('.')
558
753
        old = b.basis_tree()
559
754
        new = b.working_tree()
560
755
 
576
771
    hidden = True
577
772
    def run(self):
578
773
        import statcache
579
 
        b = Branch('.')
 
774
        b = find_branch('.')
580
775
        inv = b.read_working_inventory()
581
776
        sc = statcache.update_cache(b, inv)
582
777
        basis = b.basis_tree()
602
797
    """List files added in working tree."""
603
798
    hidden = True
604
799
    def run(self):
605
 
        b = Branch('.')
 
800
        b = find_branch('.')
606
801
        wt = b.working_tree()
607
802
        basis_inv = b.basis_tree().inventory
608
803
        inv = wt.inventory
624
819
    takes_args = ['filename?']
625
820
    def run(self, filename=None):
626
821
        """Print the branch root."""
627
 
        from branch import find_branch
628
822
        b = find_branch(filename)
629
823
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
630
824
 
641
835
    """
642
836
 
643
837
    takes_args = ['filename?']
644
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
 
838
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long']
645
839
    
646
840
    def run(self, filename=None, timezone='original',
647
841
            verbose=False,
648
842
            show_ids=False,
649
843
            forward=False,
650
 
            revision=None):
651
 
        from bzrlib import show_log, find_branch
 
844
            revision=None,
 
845
            long=False):
 
846
        from bzrlib.branch import find_branch
 
847
        from bzrlib.log import log_formatter, show_log
652
848
        import codecs
653
849
 
654
850
        direction = (forward and 'forward') or 'reverse'
680
876
        # in e.g. the default C locale.
681
877
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
682
878
 
683
 
        show_log(b, file_id,
684
 
                 show_timezone=timezone,
 
879
        if long:
 
880
            log_format = 'long'
 
881
        else:
 
882
            log_format = 'short'
 
883
        lf = log_formatter(log_format,
 
884
                           show_ids=show_ids,
 
885
                           to_file=outf,
 
886
                           show_timezone=timezone)
 
887
 
 
888
        show_log(b,
 
889
                 lf,
 
890
                 file_id,
685
891
                 verbose=verbose,
686
 
                 show_ids=show_ids,
687
 
                 to_file=outf,
688
892
                 direction=direction,
689
893
                 start_revision=revision[0],
690
894
                 end_revision=revision[1])
698
902
    hidden = True
699
903
    takes_args = ["filename"]
700
904
    def run(self, filename):
701
 
        b = Branch(filename)
 
905
        b = find_branch(filename)
702
906
        inv = b.read_working_inventory()
703
907
        file_id = inv.path2id(b.relpath(filename))
704
908
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
712
916
    """
713
917
    hidden = True
714
918
    def run(self, revision=None, verbose=False):
715
 
        b = Branch('.')
 
919
        b = find_branch('.')
716
920
        if revision == None:
717
921
            tree = b.working_tree()
718
922
        else:
734
938
 
735
939
 
736
940
class cmd_unknowns(Command):
737
 
    """List unknown files"""
 
941
    """List unknown files."""
738
942
    def run(self):
739
 
        for f in Branch('.').unknowns():
 
943
        from bzrlib.osutils import quotefn
 
944
        for f in find_branch('.').unknowns():
740
945
            print quotefn(f)
741
946
 
742
947
 
743
948
 
744
949
class cmd_ignore(Command):
745
 
    """Ignore a command or pattern
 
950
    """Ignore a command or pattern.
746
951
 
747
952
    To remove patterns from the ignore list, edit the .bzrignore file.
748
953
 
764
969
        from bzrlib.atomicfile import AtomicFile
765
970
        import os.path
766
971
 
767
 
        b = Branch('.')
 
972
        b = find_branch('.')
768
973
        ifn = b.abspath('.bzrignore')
769
974
 
770
975
        if os.path.exists(ifn):
804
1009
 
805
1010
    See also: bzr ignore"""
806
1011
    def run(self):
807
 
        tree = Branch('.').working_tree()
 
1012
        tree = find_branch('.').working_tree()
808
1013
        for path, file_class, kind, file_id in tree.list_files():
809
1014
            if file_class != 'I':
810
1015
                continue
828
1033
        except ValueError:
829
1034
            raise BzrCommandError("not a valid revision-number: %r" % revno)
830
1035
 
831
 
        print Branch('.').lookup_revision(revno)
 
1036
        print find_branch('.').lookup_revision(revno)
832
1037
 
833
1038
 
834
1039
class cmd_export(Command):
835
1040
    """Export past revision to destination directory.
836
1041
 
837
 
    If no revision is specified this exports the last committed revision."""
 
1042
    If no revision is specified this exports the last committed revision.
 
1043
 
 
1044
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
 
1045
    given, exports to a directory (equivalent to --format=dir)."""
 
1046
    # TODO: list known exporters
838
1047
    takes_args = ['dest']
839
 
    takes_options = ['revision']
840
 
    def run(self, dest, revision=None):
841
 
        b = Branch('.')
 
1048
    takes_options = ['revision', 'format']
 
1049
    def run(self, dest, revision=None, format='dir'):
 
1050
        b = find_branch('.')
842
1051
        if revision == None:
843
1052
            rh = b.revision_history()[-1]
844
1053
        else:
845
1054
            rh = b.lookup_revision(int(revision))
846
1055
        t = b.revision_tree(rh)
847
 
        t.export(dest)
 
1056
        t.export(dest, format)
848
1057
 
849
1058
 
850
1059
class cmd_cat(Command):
856
1065
    def run(self, filename, revision=None):
857
1066
        if revision == None:
858
1067
            raise BzrCommandError("bzr cat requires a revision number")
859
 
        b = Branch('.')
 
1068
        b = find_branch('.')
860
1069
        b.print_file(b.relpath(filename), int(revision))
861
1070
 
862
1071
 
889
1098
 
890
1099
    def run(self, message=None, file=None, verbose=True, selected_list=None):
891
1100
        from bzrlib.commit import commit
 
1101
        from bzrlib.osutils import get_text_message
892
1102
 
893
1103
        ## Warning: shadows builtin file()
894
1104
        if not message and not file:
895
 
            raise BzrCommandError("please specify a commit message",
896
 
                                  ["use either --message or --file"])
 
1105
            import cStringIO
 
1106
            stdout = sys.stdout
 
1107
            catcher = cStringIO.StringIO()
 
1108
            sys.stdout = catcher
 
1109
            cmd_status({"file_list":selected_list}, {})
 
1110
            info = catcher.getvalue()
 
1111
            sys.stdout = stdout
 
1112
            message = get_text_message(info)
 
1113
            
 
1114
            if message is None:
 
1115
                raise BzrCommandError("please specify a commit message",
 
1116
                                      ["use either --message or --file"])
897
1117
        elif message and file:
898
1118
            raise BzrCommandError("please specify either --message or --file")
899
1119
        
901
1121
            import codecs
902
1122
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
903
1123
 
904
 
        b = Branch('.')
 
1124
        b = find_branch('.')
905
1125
        commit(b, message, verbose=verbose, specific_files=selected_list)
906
1126
 
907
1127
 
910
1130
 
911
1131
    This command checks various invariants about the branch storage to
912
1132
    detect data corruption or bzr bugs.
913
 
    """
914
 
    takes_args = ['dir?']
915
 
    def run(self, dir='.'):
916
 
        import bzrlib.check
917
 
        bzrlib.check.check(Branch(dir))
 
1133
 
 
1134
    If given the --update flag, it will update some optional fields
 
1135
    to help ensure data consistency.
 
1136
    """
 
1137
    takes_args = ['dir?']
 
1138
 
 
1139
    def run(self, dir='.'):
 
1140
        from bzrlib.check import check
 
1141
        check(find_branch(dir))
 
1142
 
 
1143
 
 
1144
 
 
1145
class cmd_upgrade(Command):
 
1146
    """Upgrade branch storage to current format.
 
1147
 
 
1148
    This should normally be used only after the check command tells
 
1149
    you to run it.
 
1150
    """
 
1151
    takes_args = ['dir?']
 
1152
 
 
1153
    def run(self, dir='.'):
 
1154
        from bzrlib.upgrade import upgrade
 
1155
        upgrade(find_branch(dir))
918
1156
 
919
1157
 
920
1158
 
934
1172
    hidden = True
935
1173
    def run(self):
936
1174
        from bzrlib.selftest import selftest
937
 
        if selftest():
938
 
            return 0
939
 
        else:
940
 
            return 1
941
 
 
 
1175
        return int(not selftest())
942
1176
 
943
1177
 
944
1178
class cmd_version(Command):
945
 
    """Show version of bzr"""
 
1179
    """Show version of bzr."""
946
1180
    def run(self):
947
1181
        show_version()
948
1182
 
967
1201
        print "it sure does!"
968
1202
 
969
1203
def parse_spec(spec):
 
1204
    """
 
1205
    >>> parse_spec(None)
 
1206
    [None, None]
 
1207
    >>> parse_spec("./")
 
1208
    ['./', None]
 
1209
    >>> parse_spec("../@")
 
1210
    ['..', -1]
 
1211
    >>> parse_spec("../f/@35")
 
1212
    ['../f', 35]
 
1213
    """
 
1214
    if spec is None:
 
1215
        return [None, None]
970
1216
    if '/@' in spec:
971
1217
        parsed = spec.split('/@')
972
1218
        assert len(parsed) == 2
979
1225
        parsed = [spec, None]
980
1226
    return parsed
981
1227
 
 
1228
 
 
1229
 
982
1230
class cmd_merge(Command):
983
 
    """Perform a three-way merge of trees."""
984
 
    takes_args = ['other_spec', 'base_spec']
985
 
 
986
 
    def run(self, other_spec, base_spec):
987
 
        from bzrlib.merge import merge
988
 
        merge(parse_spec(other_spec), parse_spec(base_spec))
 
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
 
 
1249
    merge refuses to run if there are any uncommitted changes, unless
 
1250
    --force is given.
 
1251
    """
 
1252
    takes_args = ['other_spec', 'base_spec?']
 
1253
    takes_options = ['force']
 
1254
 
 
1255
    def run(self, other_spec, base_spec=None, force=False):
 
1256
        from bzrlib.merge import merge
 
1257
        merge(parse_spec(other_spec), parse_spec(base_spec),
 
1258
              check_clean=(not force))
 
1259
 
 
1260
 
 
1261
 
 
1262
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
    """Reverse all changes since the last commit.
 
1279
 
 
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.          
 
1284
    """
 
1285
    takes_options = ['revision']
 
1286
 
 
1287
    def run(self, revision=-1):
 
1288
        from bzrlib.merge import merge
 
1289
        merge(('.', revision), parse_spec('.'),
 
1290
              check_clean=False,
 
1291
              ignore_zero=True)
 
1292
 
989
1293
 
990
1294
class cmd_assert_fail(Command):
991
1295
    """Test reporting of assertion failures"""
1013
1317
    hidden = True
1014
1318
    def run(self):
1015
1319
        import statcache
1016
 
        b = Branch('.')
 
1320
        b = find_branch('.')
1017
1321
        statcache.update_cache(b.base, b.read_working_inventory())
1018
1322
 
1019
1323
 
1020
1324
 
 
1325
class cmd_plugins(Command):
 
1326
    """List plugins"""
 
1327
    hidden = True
 
1328
    def run(self):
 
1329
        import bzrlib.plugin
 
1330
        from pprint import pprint
 
1331
        pprint(bzrlib.plugin.all_plugins)
 
1332
 
 
1333
 
 
1334
 
1021
1335
# list of all available options; the rhs can be either None for an
1022
1336
# option that takes no argument, or a constructor function that checks
1023
1337
# the type.
1026
1340
    'diff-options':           str,
1027
1341
    'help':                   None,
1028
1342
    'file':                   unicode,
 
1343
    'force':                  None,
 
1344
    'format':                 unicode,
1029
1345
    'forward':                None,
1030
1346
    'message':                unicode,
1031
1347
    'no-recurse':             None,
1036
1352
    'verbose':                None,
1037
1353
    'version':                None,
1038
1354
    'email':                  None,
 
1355
    'update':                 None,
 
1356
    'long':                   None,
1039
1357
    }
1040
1358
 
1041
1359
SHORT_OPTIONS = {
1044
1362
    'm':                      'message',
1045
1363
    'r':                      'revision',
1046
1364
    'v':                      'verbose',
 
1365
    'l':                      'long',
1047
1366
}
1048
1367
 
1049
1368
 
1063
1382
    (['status'], {'all': True})
1064
1383
    >>> parse_args('commit --message=biter'.split())
1065
1384
    (['commit'], {'message': u'biter'})
 
1385
    >>> parse_args('log -r 500'.split())
 
1386
    (['log'], {'revision': 500})
 
1387
    >>> parse_args('log -r500:600'.split())
 
1388
    (['log'], {'revision': [500, 600]})
 
1389
    >>> parse_args('log -vr500:600'.split())
 
1390
    (['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
1066
1395
    """
1067
1396
    args = []
1068
1397
    opts = {}
1082
1411
                else:
1083
1412
                    optname = a[2:]
1084
1413
                if optname not in OPTIONS:
1085
 
                    bailout('unknown long option %r' % a)
 
1414
                    raise BzrError('unknown long option %r' % a)
1086
1415
            else:
1087
1416
                shortopt = a[1:]
1088
 
                if shortopt not in SHORT_OPTIONS:
1089
 
                    bailout('unknown short option %r' % a)
1090
 
                optname = SHORT_OPTIONS[shortopt]
 
1417
                if shortopt in SHORT_OPTIONS:
 
1418
                    # Multi-character options must have a space to delimit
 
1419
                    # their value
 
1420
                    optname = SHORT_OPTIONS[shortopt]
 
1421
                else:
 
1422
                    # Single character short options, can be chained,
 
1423
                    # and have their value appended to their name
 
1424
                    shortopt = a[1:2]
 
1425
                    if shortopt not in SHORT_OPTIONS:
 
1426
                        # We didn't find the multi-character name, and we
 
1427
                        # didn't find the single char name
 
1428
                        raise BzrError('unknown short option %r' % a)
 
1429
                    optname = SHORT_OPTIONS[shortopt]
 
1430
 
 
1431
                    if a[2:]:
 
1432
                        # There are extra things on this option
 
1433
                        # see if it is the value, or if it is another
 
1434
                        # short option
 
1435
                        optargfn = OPTIONS[optname]
 
1436
                        if optargfn is None:
 
1437
                            # This option does not take an argument, so the
 
1438
                            # next entry is another short option, pack it back
 
1439
                            # into the list
 
1440
                            argv.insert(0, '-' + a[2:])
 
1441
                        else:
 
1442
                            # This option takes an argument, so pack it
 
1443
                            # into the array
 
1444
                            optarg = a[2:]
1091
1445
            
1092
1446
            if optname in opts:
1093
1447
                # XXX: Do we ever want to support this, e.g. for -r?
1094
 
                bailout('repeated option %r' % a)
 
1448
                raise BzrError('repeated option %r' % a)
1095
1449
                
1096
1450
            optargfn = OPTIONS[optname]
1097
1451
            if optargfn:
1098
1452
                if optarg == None:
1099
1453
                    if not argv:
1100
 
                        bailout('option %r needs an argument' % a)
 
1454
                        raise BzrError('option %r needs an argument' % a)
1101
1455
                    else:
1102
1456
                        optarg = argv.pop(0)
1103
1457
                opts[optname] = optargfn(optarg)
1104
1458
            else:
1105
1459
                if optarg != None:
1106
 
                    bailout('option %r takes no argument' % optname)
 
1460
                    raise BzrError('option %r takes no argument' % optname)
1107
1461
                opts[optname] = True
1108
1462
        else:
1109
1463
            args.append(a)
1157
1511
    return argdict
1158
1512
 
1159
1513
 
 
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
 
1160
1585
 
1161
1586
def run_bzr(argv):
1162
1587
    """Execute a command.
1167
1592
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1168
1593
    
1169
1594
    try:
1170
 
        args, opts = parse_args(argv[1:])
 
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
            
1171
1611
        if 'help' in opts:
1172
 
            import help
 
1612
            from bzrlib.help import help
1173
1613
            if args:
1174
 
                help.help(args[0])
 
1614
                help(args[0])
1175
1615
            else:
1176
 
                help.help()
 
1616
                help()
1177
1617
            return 0
1178
1618
        elif 'version' in opts:
1179
1619
            show_version()
1180
1620
            return 0
 
1621
        elif args and args[0] == 'builtin':
 
1622
            include_plugins=False
 
1623
            args = args[1:]
1181
1624
        cmd = str(args.pop(0))
1182
1625
    except IndexError:
1183
1626
        import help
1185
1628
        return 1
1186
1629
          
1187
1630
 
1188
 
    canonical_cmd, cmd_class = get_cmd_class(cmd)
 
1631
    plugins_override = not (master_opts['builtin'])
 
1632
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1189
1633
 
1190
 
    # global option
 
1634
    profile = master_opts['profile']
 
1635
    # For backwards compatibility, I would rather stick with --profile being a
 
1636
    # master/global option
1191
1637
    if 'profile' in opts:
1192
1638
        profile = True
1193
1639
        del opts['profile']
1194
 
    else:
1195
 
        profile = False
1196
1640
 
1197
1641
    # check options are reasonable
1198
1642
    allowed = cmd_class.takes_options
1247
1691
 
1248
1692
 
1249
1693
def main(argv):
1250
 
    import errno
1251
1694
    
1252
 
    bzrlib.open_tracefile(argv)
 
1695
    bzrlib.trace.open_tracefile(argv)
1253
1696
 
1254
1697
    try:
1255
1698
        try:
1276
1719
            _report_exception('interrupted', quiet=True)
1277
1720
            return 2
1278
1721
        except Exception, e:
 
1722
            import errno
1279
1723
            quiet = False
1280
1724
            if (isinstance(e, IOError) 
1281
1725
                and hasattr(e, 'errno')