~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-06-22 06:37:43 UTC
  • Revision ID: mbp@sourcefrog.net-20050622063743-e395f04c4db8977f
- move old blackbox code from testbzr into bzrlib.selftest.blackbox

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
 
import sys
31
 
import os
 
18
 
 
19
import sys, os
32
20
 
33
21
import bzrlib
34
 
from bzrlib.trace import mutter, note, log_error, warning
 
22
from bzrlib.trace import mutter, note, log_error
35
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
36
 
from bzrlib.branch import find_branch
37
 
from bzrlib import BZRDIR
 
24
from bzrlib.osutils import quotefn
 
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
 
26
     format_date
38
27
 
39
28
 
40
29
plugin_cmds = {}
41
30
 
42
31
 
43
 
def register_command(cmd):
 
32
def register_plugin_command(cmd):
44
33
    "Utility function to help register a command"
45
34
    global plugin_cmds
46
35
    k = cmd.__name__
63
52
    assert cmd.startswith("cmd_")
64
53
    return cmd[4:].replace('_','-')
65
54
 
66
 
 
67
55
def _parse_revision_str(revstr):
68
 
    """This handles a revision string -> revno.
69
 
 
70
 
    This always returns a list.  The list will have one element for 
71
 
 
72
 
    It supports integers directly, but everything else it
73
 
    defers for passing to Branch.get_revision_info()
74
 
 
75
 
    >>> _parse_revision_str('234')
76
 
    [234]
77
 
    >>> _parse_revision_str('234..567')
78
 
    [234, 567]
79
 
    >>> _parse_revision_str('..')
80
 
    [None, None]
81
 
    >>> _parse_revision_str('..234')
82
 
    [None, 234]
83
 
    >>> _parse_revision_str('234..')
84
 
    [234, None]
85
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
86
 
    [234, 456, 789]
87
 
    >>> _parse_revision_str('234....789') # Error?
88
 
    [234, None, 789]
89
 
    >>> _parse_revision_str('revid:test@other.com-234234')
90
 
    ['revid:test@other.com-234234']
91
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
92
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
93
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
94
 
    ['revid:test@other.com-234234', 23]
95
 
    >>> _parse_revision_str('date:2005-04-12')
96
 
    ['date:2005-04-12']
97
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
98
 
    ['date:2005-04-12 12:24:33']
99
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
100
 
    ['date:2005-04-12T12:24:33']
101
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
102
 
    ['date:2005-04-12,12:24:33']
103
 
    >>> _parse_revision_str('-5..23')
104
 
    [-5, 23]
105
 
    >>> _parse_revision_str('-5')
106
 
    [-5]
107
 
    >>> _parse_revision_str('123a')
108
 
    ['123a']
109
 
    >>> _parse_revision_str('abc')
110
 
    ['abc']
 
56
    """This handles a revision string -> revno. 
 
57
 
 
58
    There are several possibilities:
 
59
 
 
60
        '234'       -> 234
 
61
        '234:345'   -> [234, 345]
 
62
        ':234'      -> [None, 234]
 
63
        '234:'      -> [234, None]
 
64
 
 
65
    In the future we will also support:
 
66
        'uuid:blah-blah-blah'   -> ?
 
67
        'hash:blahblahblah'     -> ?
 
68
        potentially:
 
69
        'tag:mytag'             -> ?
111
70
    """
112
 
    import re
113
 
    old_format_re = re.compile('\d*:\d*')
114
 
    m = old_format_re.match(revstr)
115
 
    if m:
116
 
        warning('Colon separator for revision numbers is deprecated.'
117
 
                ' Use .. instead')
118
 
        revs = []
119
 
        for rev in revstr.split(':'):
120
 
            if rev:
121
 
                revs.append(int(rev))
122
 
            else:
123
 
                revs.append(None)
124
 
        return revs
125
 
    revs = []
126
 
    for x in revstr.split('..'):
127
 
        if not x:
128
 
            revs.append(None)
129
 
        else:
130
 
            try:
131
 
                revs.append(int(x))
132
 
            except ValueError:
133
 
                revs.append(x)
 
71
    if revstr.find(':') != -1:
 
72
        revs = revstr.split(':')
 
73
        if len(revs) > 2:
 
74
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
 
75
 
 
76
        if not revs[0]:
 
77
            revs[0] = None
 
78
        else:
 
79
            revs[0] = int(revs[0])
 
80
 
 
81
        if not revs[1]:
 
82
            revs[1] = None
 
83
        else:
 
84
            revs[1] = int(revs[1])
 
85
    else:
 
86
        revs = int(revstr)
134
87
    return revs
135
88
 
136
89
 
137
 
def get_merge_type(typestring):
138
 
    """Attempt to find the merge class/factory associated with a string."""
139
 
    from merge import merge_types
140
 
    try:
141
 
        return merge_types[typestring][0]
142
 
    except KeyError:
143
 
        templ = '%s%%7s: %%s' % (' '*12)
144
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
145
 
        type_list = '\n'.join(lines)
146
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
147
 
            (typestring, type_list)
148
 
        raise BzrCommandError(msg)
149
 
    
150
 
 
151
 
def get_merge_type(typestring):
152
 
    """Attempt to find the merge class/factory associated with a string."""
153
 
    from merge import merge_types
154
 
    try:
155
 
        return merge_types[typestring][0]
156
 
    except KeyError:
157
 
        templ = '%s%%7s: %%s' % (' '*12)
158
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
159
 
        type_list = '\n'.join(lines)
160
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
161
 
            (typestring, type_list)
162
 
        raise BzrCommandError(msg)
163
 
    
164
 
 
165
90
 
166
91
def _get_cmd_dict(plugins_override=True):
167
92
    d = {}
240
165
        assert isinstance(arguments, dict)
241
166
        cmdargs = options.copy()
242
167
        cmdargs.update(arguments)
243
 
        if self.__doc__ == Command.__doc__:
244
 
            from warnings import warn
245
 
            warn("No help message set for %r" % self)
 
168
        assert self.__doc__ != Command.__doc__, \
 
169
               ("No help message set for %r" % self)
246
170
        self.status = self.run(**cmdargs)
247
 
        if self.status is None:
248
 
            self.status = 0
249
171
 
250
172
    
251
173
    def run(self):
263
185
class ExternalCommand(Command):
264
186
    """Class to wrap external commands.
265
187
 
266
 
    We cheat a little here, when get_cmd_class() calls us we actually
267
 
    give it back an object we construct that has the appropriate path,
268
 
    help, options etc for the specified command.
269
 
 
270
 
    When run_bzr() tries to instantiate that 'class' it gets caught by
271
 
    the __call__ method, which we override to call the Command.__init__
272
 
    method. That then calls our run method which is pretty straight
273
 
    forward.
274
 
 
275
 
    The only wrinkle is that we have to map bzr's dictionary of options
276
 
    and arguments back into command line options and arguments for the
277
 
    script.
 
188
    We cheat a little here, when get_cmd_class() calls us we actually give it back
 
189
    an object we construct that has the appropriate path, help, options etc for the
 
190
    specified command.
 
191
 
 
192
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
 
193
    method, which we override to call the Command.__init__ method. That then calls
 
194
    our run method which is pretty straight forward.
 
195
 
 
196
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
 
197
    back into command line options and arguments for the script.
278
198
    """
279
199
 
280
200
    def find_command(cls, cmd):
377
297
    directory is shown.  Otherwise, only the status of the specified
378
298
    files or directories is reported.  If a directory is given, status
379
299
    is reported for everything inside that directory.
380
 
 
381
 
    If a revision is specified, the changes since that revision are shown.
382
300
    """
383
301
    takes_args = ['file*']
384
 
    takes_options = ['all', 'show-ids', 'revision']
 
302
    takes_options = ['all', 'show-ids']
385
303
    aliases = ['st', 'stat']
386
304
    
387
305
    def run(self, all=False, show_ids=False, file_list=None):
388
306
        if file_list:
389
 
            b = find_branch(file_list[0])
 
307
            b = Branch(file_list[0])
390
308
            file_list = [b.relpath(x) for x in file_list]
391
309
            # special case: only one path was given and it's the root
392
310
            # of the branch
393
311
            if file_list == ['']:
394
312
                file_list = None
395
313
        else:
396
 
            b = find_branch('.')
397
 
            
398
 
        from bzrlib.status import show_status
399
 
        show_status(b, show_unchanged=all, show_ids=show_ids,
400
 
                    specific_files=file_list)
 
314
            b = Branch('.')
 
315
        import status
 
316
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
 
317
                           specific_files=file_list)
401
318
 
402
319
 
403
320
class cmd_cat_revision(Command):
407
324
    takes_args = ['revision_id']
408
325
    
409
326
    def run(self, revision_id):
410
 
        from bzrlib.xml import pack_xml
411
 
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
 
327
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
412
328
 
413
329
 
414
330
class cmd_revno(Command):
416
332
 
417
333
    This is equal to the number of revisions on this branch."""
418
334
    def run(self):
419
 
        print find_branch('.').revno()
420
 
 
421
 
class cmd_revision_info(Command):
422
 
    """Show revision number and revision id for a given revision identifier.
423
 
    """
424
 
    hidden = True
425
 
    takes_args = ['revision_info*']
426
 
    takes_options = ['revision']
427
 
    def run(self, revision=None, revision_info_list=None):
428
 
        from bzrlib.branch import find_branch
429
 
 
430
 
        revs = []
431
 
        if revision is not None:
432
 
            revs.extend(revision)
433
 
        if revision_info_list is not None:
434
 
            revs.extend(revision_info_list)
435
 
        if len(revs) == 0:
436
 
            raise BzrCommandError('You must supply a revision identifier')
437
 
 
438
 
        b = find_branch('.')
439
 
 
440
 
        for rev in revs:
441
 
            print '%4d %s' % b.get_revision_info(rev)
 
335
        print Branch('.').revno()
442
336
 
443
337
    
444
338
class cmd_add(Command):
454
348
    whether already versioned or not, are searched for files or
455
349
    subdirectories that are neither versioned or ignored, and these
456
350
    are added.  This search proceeds recursively into versioned
457
 
    directories.  If no names are given '.' is assumed.
 
351
    directories.
458
352
 
459
 
    Therefore simply saying 'bzr add' will version all files that
 
353
    Therefore simply saying 'bzr add .' will version all files that
460
354
    are currently unknown.
461
355
 
462
356
    TODO: Perhaps adding a file whose directly is not versioned should
463
357
    recursively add that parent, rather than giving an error?
464
358
    """
465
 
    takes_args = ['file*']
 
359
    takes_args = ['file+']
466
360
    takes_options = ['verbose', 'no-recurse']
467
361
    
468
362
    def run(self, file_list, verbose=False, no_recurse=False):
469
 
        from bzrlib.add import smart_add
470
 
        smart_add(file_list, verbose, not no_recurse)
471
 
 
472
 
 
473
 
 
474
 
class cmd_mkdir(Command):
475
 
    """Create a new versioned directory.
476
 
 
477
 
    This is equivalent to creating the directory and then adding it.
478
 
    """
479
 
    takes_args = ['dir+']
480
 
 
481
 
    def run(self, dir_list):
482
 
        b = None
483
 
        
484
 
        for d in dir_list:
485
 
            os.mkdir(d)
486
 
            if not b:
487
 
                b = find_branch(d)
488
 
            b.add([d], verbose=True)
 
363
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
489
364
 
490
365
 
491
366
class cmd_relpath(Command):
494
369
    hidden = True
495
370
    
496
371
    def run(self, filename):
497
 
        print find_branch(filename).relpath(filename)
 
372
        print Branch(filename).relpath(filename)
498
373
 
499
374
 
500
375
 
503
378
    takes_options = ['revision', 'show-ids']
504
379
    
505
380
    def run(self, revision=None, show_ids=False):
506
 
        b = find_branch('.')
 
381
        b = Branch('.')
507
382
        if revision == None:
508
383
            inv = b.read_working_inventory()
509
384
        else:
510
 
            if len(revision) > 1:
511
 
                raise BzrCommandError('bzr inventory --revision takes'
512
 
                    ' exactly one revision identifier')
513
 
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
 
385
            inv = b.get_revision_inventory(b.lookup_revision(revision))
514
386
 
515
387
        for path, entry in inv.entries():
516
388
            if show_ids:
529
401
    """
530
402
    takes_args = ['source$', 'dest']
531
403
    def run(self, source_list, dest):
532
 
        b = find_branch('.')
 
404
        b = Branch('.')
533
405
 
534
 
        # TODO: glob expansion on windows?
535
406
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
536
407
 
537
408
 
552
423
    takes_args = ['from_name', 'to_name']
553
424
    
554
425
    def run(self, from_name, to_name):
555
 
        b = find_branch('.')
 
426
        b = Branch('.')
556
427
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
557
428
 
558
429
 
559
430
 
560
 
class cmd_mv(Command):
561
 
    """Move or rename a file.
562
 
 
563
 
    usage:
564
 
        bzr mv OLDNAME NEWNAME
565
 
        bzr mv SOURCE... DESTINATION
566
 
 
567
 
    If the last argument is a versioned directory, all the other names
568
 
    are moved into it.  Otherwise, there must be exactly two arguments
569
 
    and the file is changed to a new name, which must not already exist.
570
 
 
571
 
    Files cannot be moved between branches.
572
 
    """
573
 
    takes_args = ['names*']
574
 
    def run(self, names_list):
575
 
        if len(names_list) < 2:
576
 
            raise BzrCommandError("missing file argument")
577
 
        b = find_branch(names_list[0])
578
 
 
579
 
        rel_names = [b.relpath(x) for x in names_list]
580
 
        
581
 
        if os.path.isdir(names_list[-1]):
582
 
            # move into existing directory
583
 
            b.move(rel_names[:-1], rel_names[-1])
584
 
        else:
585
 
            if len(names_list) != 2:
586
 
                raise BzrCommandError('to mv multiple files the destination '
587
 
                                      'must be a versioned directory')
588
 
            b.move(rel_names[0], rel_names[1])
589
 
            
590
 
    
591
431
 
592
432
 
593
433
class cmd_pull(Command):
608
448
 
609
449
    def run(self, location=None):
610
450
        from bzrlib.merge import merge
611
 
        import tempfile
612
 
        from shutil import rmtree
613
451
        import errno
614
452
        
615
 
        br_to = find_branch('.')
 
453
        br_to = Branch('.')
616
454
        stored_loc = None
617
455
        try:
618
456
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
619
457
        except IOError, e:
620
 
            if e.errno != errno.ENOENT:
 
458
            if errno == errno.ENOENT:
621
459
                raise
622
460
        if location is None:
623
461
            if stored_loc is None:
625
463
            else:
626
464
                print "Using last location: %s" % stored_loc
627
465
                location = stored_loc
628
 
        cache_root = tempfile.mkdtemp()
629
 
        from bzrlib.branch import DivergedBranches
 
466
        from branch import find_branch, DivergedBranches
630
467
        br_from = find_branch(location)
631
468
        location = pull_loc(br_from)
632
469
        old_revno = br_to.revno()
633
470
        try:
634
 
            from branch import find_cached_branch, DivergedBranches
635
 
            br_from = find_cached_branch(location, cache_root)
636
 
            location = pull_loc(br_from)
637
 
            old_revno = br_to.revno()
638
 
            try:
639
 
                br_to.update_revisions(br_from)
640
 
            except DivergedBranches:
641
 
                raise BzrCommandError("These branches have diverged."
642
 
                    "  Try merge.")
643
 
                
644
 
            merge(('.', -1), ('.', old_revno), check_clean=False)
645
 
            if location != stored_loc:
646
 
                br_to.controlfile("x-pull", "wb").write(location + "\n")
647
 
        finally:
648
 
            rmtree(cache_root)
 
471
            br_to.update_revisions(br_from)
 
472
        except DivergedBranches:
 
473
            raise BzrCommandError("These branches have diverged.  Try merge.")
 
474
            
 
475
        merge(('.', -1), ('.', old_revno), check_clean=False)
 
476
        if location != stored_loc:
 
477
            br_to.controlfile("x-pull", "wb").write(location + "\n")
649
478
 
650
479
 
651
480
 
660
489
    """
661
490
    takes_args = ['from_location', 'to_location?']
662
491
    takes_options = ['revision']
663
 
    aliases = ['get', 'clone']
664
492
 
665
493
    def run(self, from_location, to_location=None, revision=None):
666
494
        import errno
667
495
        from bzrlib.merge import merge
668
 
        from bzrlib.branch import DivergedBranches, \
669
 
             find_cached_branch, Branch
 
496
        from branch import find_branch, DivergedBranches, NoSuchRevision
670
497
        from shutil import rmtree
671
 
        from meta_store import CachedStore
672
 
        import tempfile
673
 
        cache_root = tempfile.mkdtemp()
674
 
 
675
 
        if revision is None:
676
 
            revision = [None]
677
 
        elif len(revision) > 1:
678
 
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
679
 
 
680
 
        try:
681
 
            try:
682
 
                br_from = find_cached_branch(from_location, cache_root)
683
 
            except OSError, e:
684
 
                if e.errno == errno.ENOENT:
685
 
                    raise BzrCommandError('Source location "%s" does not'
686
 
                                          ' exist.' % to_location)
687
 
                else:
688
 
                    raise
689
 
 
690
 
            if to_location is None:
691
 
                to_location = os.path.basename(from_location.rstrip("/\\"))
692
 
 
693
 
            try:
694
 
                os.mkdir(to_location)
695
 
            except OSError, e:
696
 
                if e.errno == errno.EEXIST:
697
 
                    raise BzrCommandError('Target directory "%s" already'
698
 
                                          ' exists.' % to_location)
699
 
                if e.errno == errno.ENOENT:
700
 
                    raise BzrCommandError('Parent of "%s" does not exist.' %
701
 
                                          to_location)
702
 
                else:
703
 
                    raise
704
 
            br_to = Branch(to_location, init=True)
705
 
 
706
 
            br_to.set_root_id(br_from.get_root_id())
707
 
 
708
 
            if revision:
709
 
                if revision[0] is None:
710
 
                    revno = br_from.revno()
711
 
                else:
712
 
                    revno, rev_id = br_from.get_revision_info(revision[0])
713
 
                try:
714
 
                    br_to.update_revisions(br_from, stop_revision=revno)
715
 
                except bzrlib.errors.NoSuchRevision:
716
 
                    rmtree(to_location)
717
 
                    msg = "The branch %s has no revision %d." % (from_location,
718
 
                                                                 revno)
719
 
                    raise BzrCommandError(msg)
720
 
 
721
 
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
722
 
                  check_clean=False, ignore_zero=True)
723
 
            from_location = pull_loc(br_from)
724
 
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
725
 
        finally:
726
 
            rmtree(cache_root)
 
498
        try:
 
499
            br_from = find_branch(from_location)
 
500
        except OSError, e:
 
501
            if e.errno == errno.ENOENT:
 
502
                raise BzrCommandError('Source location "%s" does not exist.' %
 
503
                                      to_location)
 
504
            else:
 
505
                raise
 
506
 
 
507
        if to_location is None:
 
508
            to_location = os.path.basename(from_location.rstrip("/\\"))
 
509
 
 
510
        try:
 
511
            os.mkdir(to_location)
 
512
        except OSError, e:
 
513
            if e.errno == errno.EEXIST:
 
514
                raise BzrCommandError('Target directory "%s" already exists.' %
 
515
                                      to_location)
 
516
            if e.errno == errno.ENOENT:
 
517
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
518
                                      to_location)
 
519
            else:
 
520
                raise
 
521
        br_to = Branch(to_location, init=True)
 
522
 
 
523
        try:
 
524
            br_to.update_revisions(br_from, stop_revision=revision)
 
525
        except NoSuchRevision:
 
526
            rmtree(to_location)
 
527
            msg = "The branch %s has no revision %d." % (from_location,
 
528
                                                         revision)
 
529
            raise BzrCommandError(msg)
 
530
        merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
531
              check_clean=False, ignore_zero=True)
 
532
        from_location = pull_loc(br_from)
 
533
        br_to.controlfile("x-pull", "wb").write(from_location + "\n")
727
534
 
728
535
 
729
536
def pull_loc(branch):
746
553
    takes_args = ['dir?']
747
554
 
748
555
    def run(self, dir='.'):
749
 
        b = find_branch(dir)
 
556
        b = Branch(dir)
750
557
        old_inv = b.basis_tree().inventory
751
558
        new_inv = b.read_working_inventory()
752
559
 
763
570
    def run(self, branch=None):
764
571
        import info
765
572
 
 
573
        from branch import find_branch
766
574
        b = find_branch(branch)
767
575
        info.show_info(b)
768
576
 
777
585
    takes_options = ['verbose']
778
586
    
779
587
    def run(self, file_list, verbose=False):
780
 
        b = find_branch(file_list[0])
 
588
        b = Branch(file_list[0])
781
589
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
782
590
 
783
591
 
791
599
    hidden = True
792
600
    takes_args = ['filename']
793
601
    def run(self, filename):
794
 
        b = find_branch(filename)
 
602
        b = Branch(filename)
795
603
        i = b.inventory.path2id(b.relpath(filename))
796
604
        if i == None:
797
605
            raise BzrError("%r is not a versioned file" % filename)
807
615
    hidden = True
808
616
    takes_args = ['filename']
809
617
    def run(self, filename):
810
 
        b = find_branch(filename)
 
618
        b = Branch(filename)
811
619
        inv = b.inventory
812
620
        fid = inv.path2id(b.relpath(filename))
813
621
        if fid == None:
820
628
    """Display list of revision ids on this branch."""
821
629
    hidden = True
822
630
    def run(self):
823
 
        for patchid in find_branch('.').revision_history():
 
631
        for patchid in Branch('.').revision_history():
824
632
            print patchid
825
633
 
826
634
 
827
635
class cmd_directories(Command):
828
636
    """Display list of versioned directories in this branch."""
829
637
    def run(self):
830
 
        for name, ie in find_branch('.').read_working_inventory().directories():
 
638
        for name, ie in Branch('.').read_working_inventory().directories():
831
639
            if name == '':
832
640
                print '.'
833
641
            else:
848
656
        bzr commit -m 'imported project'
849
657
    """
850
658
    def run(self):
851
 
        from bzrlib.branch import Branch
852
659
        Branch('.', init=True)
853
660
 
854
661
 
858
665
    If files are listed, only the changes in those files are listed.
859
666
    Otherwise, all changes for the tree are listed.
860
667
 
 
668
    TODO: Given two revision arguments, show the difference between them.
 
669
 
861
670
    TODO: Allow diff across branches.
862
671
 
863
672
    TODO: Option to use external diff command; could be GNU diff, wdiff,
872
681
          deleted files.
873
682
 
874
683
    TODO: This probably handles non-Unix newlines poorly.
875
 
 
876
 
    examples:
877
 
        bzr diff
878
 
        bzr diff -r1
879
 
        bzr diff -r1:2
880
684
    """
881
685
    
882
686
    takes_args = ['file*']
885
689
 
886
690
    def run(self, revision=None, file_list=None, diff_options=None):
887
691
        from bzrlib.diff import show_diff
 
692
        from bzrlib import find_branch
888
693
 
889
694
        if file_list:
890
695
            b = find_branch(file_list[0])
893
698
                # just pointing to top-of-tree
894
699
                file_list = None
895
700
        else:
896
 
            b = find_branch('.')
 
701
            b = Branch('.')
 
702
    
 
703
        show_diff(b, revision, specific_files=file_list,
 
704
                  external_diff_options=diff_options)
897
705
 
898
 
        if revision is not None:
899
 
            if len(revision) == 1:
900
 
                show_diff(b, revision[0], specific_files=file_list,
901
 
                          external_diff_options=diff_options)
902
 
            elif len(revision) == 2:
903
 
                show_diff(b, revision[0], specific_files=file_list,
904
 
                          external_diff_options=diff_options,
905
 
                          revision2=revision[1])
906
 
            else:
907
 
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
908
 
        else:
909
 
            show_diff(b, None, specific_files=file_list,
910
 
                      external_diff_options=diff_options)
911
706
 
912
707
        
913
708
 
918
713
    TODO: Show files deleted since a previous revision, or between two revisions.
919
714
    """
920
715
    def run(self, show_ids=False):
921
 
        b = find_branch('.')
 
716
        b = Branch('.')
922
717
        old = b.basis_tree()
923
718
        new = b.working_tree()
924
719
 
939
734
    """List files modified in working tree."""
940
735
    hidden = True
941
736
    def run(self):
942
 
        from bzrlib.delta import compare_trees
943
 
 
944
 
        b = find_branch('.')
945
 
        td = compare_trees(b.basis_tree(), b.working_tree())
946
 
 
947
 
        for path, id, kind in td.modified:
948
 
            print path
 
737
        import statcache
 
738
        b = Branch('.')
 
739
        inv = b.read_working_inventory()
 
740
        sc = statcache.update_cache(b, inv)
 
741
        basis = b.basis_tree()
 
742
        basis_inv = basis.inventory
 
743
        
 
744
        # We used to do this through iter_entries(), but that's slow
 
745
        # when most of the files are unmodified, as is usually the
 
746
        # case.  So instead we iterate by inventory entry, and only
 
747
        # calculate paths as necessary.
 
748
 
 
749
        for file_id in basis_inv:
 
750
            cacheentry = sc.get(file_id)
 
751
            if not cacheentry:                 # deleted
 
752
                continue
 
753
            ie = basis_inv[file_id]
 
754
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
 
755
                path = inv.id2path(file_id)
 
756
                print path
949
757
 
950
758
 
951
759
 
953
761
    """List files added in working tree."""
954
762
    hidden = True
955
763
    def run(self):
956
 
        b = find_branch('.')
 
764
        b = Branch('.')
957
765
        wt = b.working_tree()
958
766
        basis_inv = b.basis_tree().inventory
959
767
        inv = wt.inventory
975
783
    takes_args = ['filename?']
976
784
    def run(self, filename=None):
977
785
        """Print the branch root."""
 
786
        from branch import find_branch
978
787
        b = find_branch(filename)
979
788
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
980
789
 
986
795
    -r revision requests a specific revision, -r :end or -r begin: are
987
796
    also valid.
988
797
 
989
 
    --message allows you to give a regular expression, which will be evaluated
990
 
    so that only matching entries will be displayed.
991
 
 
992
798
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
993
799
  
994
800
    """
995
801
 
996
802
    takes_args = ['filename?']
997
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
998
 
                     'long', 'message', 'short',]
 
803
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
999
804
    
1000
805
    def run(self, filename=None, timezone='original',
1001
806
            verbose=False,
1002
807
            show_ids=False,
1003
808
            forward=False,
1004
 
            revision=None,
1005
 
            message=None,
1006
 
            long=False,
1007
 
            short=False):
1008
 
        from bzrlib.branch import find_branch
1009
 
        from bzrlib.log import log_formatter, show_log
 
809
            revision=None):
 
810
        from bzrlib import show_log, find_branch
1010
811
        import codecs
1011
812
 
1012
813
        direction = (forward and 'forward') or 'reverse'
1022
823
            b = find_branch('.')
1023
824
            file_id = None
1024
825
 
1025
 
        if revision is None:
1026
 
            rev1 = None
1027
 
            rev2 = None
1028
 
        elif len(revision) == 1:
1029
 
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
1030
 
        elif len(revision) == 2:
1031
 
            rev1 = b.get_revision_info(revision[0])[0]
1032
 
            rev2 = b.get_revision_info(revision[1])[0]
 
826
        if revision == None:
 
827
            revision = [None, None]
 
828
        elif isinstance(revision, int):
 
829
            revision = [revision, revision]
1033
830
        else:
1034
 
            raise BzrCommandError('bzr log --revision takes one or two values.')
1035
 
 
1036
 
        if rev1 == 0:
1037
 
            rev1 = None
1038
 
        if rev2 == 0:
1039
 
            rev2 = None
 
831
            # pair of revisions?
 
832
            pass
 
833
            
 
834
        assert len(revision) == 2
1040
835
 
1041
836
        mutter('encoding log as %r' % bzrlib.user_encoding)
1042
837
 
1044
839
        # in e.g. the default C locale.
1045
840
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1046
841
 
1047
 
        if not short:
1048
 
            log_format = 'long'
1049
 
        else:
1050
 
            log_format = 'short'
1051
 
        lf = log_formatter(log_format,
1052
 
                           show_ids=show_ids,
1053
 
                           to_file=outf,
1054
 
                           show_timezone=timezone)
1055
 
 
1056
 
        show_log(b,
1057
 
                 lf,
1058
 
                 file_id,
 
842
        show_log(b, file_id,
 
843
                 show_timezone=timezone,
1059
844
                 verbose=verbose,
 
845
                 show_ids=show_ids,
 
846
                 to_file=outf,
1060
847
                 direction=direction,
1061
 
                 start_revision=rev1,
1062
 
                 end_revision=rev2,
1063
 
                 search=message)
 
848
                 start_revision=revision[0],
 
849
                 end_revision=revision[1])
1064
850
 
1065
851
 
1066
852
 
1071
857
    hidden = True
1072
858
    takes_args = ["filename"]
1073
859
    def run(self, filename):
1074
 
        b = find_branch(filename)
 
860
        b = Branch(filename)
1075
861
        inv = b.read_working_inventory()
1076
862
        file_id = inv.path2id(b.relpath(filename))
1077
863
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1085
871
    """
1086
872
    hidden = True
1087
873
    def run(self, revision=None, verbose=False):
1088
 
        b = find_branch('.')
 
874
        b = Branch('.')
1089
875
        if revision == None:
1090
876
            tree = b.working_tree()
1091
877
        else:
1109
895
class cmd_unknowns(Command):
1110
896
    """List unknown files."""
1111
897
    def run(self):
1112
 
        from bzrlib.osutils import quotefn
1113
 
        for f in find_branch('.').unknowns():
 
898
        for f in Branch('.').unknowns():
1114
899
            print quotefn(f)
1115
900
 
1116
901
 
1138
923
        from bzrlib.atomicfile import AtomicFile
1139
924
        import os.path
1140
925
 
1141
 
        b = find_branch('.')
 
926
        b = Branch('.')
1142
927
        ifn = b.abspath('.bzrignore')
1143
928
 
1144
929
        if os.path.exists(ifn):
1178
963
 
1179
964
    See also: bzr ignore"""
1180
965
    def run(self):
1181
 
        tree = find_branch('.').working_tree()
 
966
        tree = Branch('.').working_tree()
1182
967
        for path, file_class, kind, file_id in tree.list_files():
1183
968
            if file_class != 'I':
1184
969
                continue
1202
987
        except ValueError:
1203
988
            raise BzrCommandError("not a valid revision-number: %r" % revno)
1204
989
 
1205
 
        print find_branch('.').lookup_revision(revno)
 
990
        print Branch('.').lookup_revision(revno)
1206
991
 
1207
992
 
1208
993
class cmd_export(Command):
1211
996
    If no revision is specified this exports the last committed revision.
1212
997
 
1213
998
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1214
 
    given, try to find the format with the extension. If no extension
1215
 
    is found exports to a directory (equivalent to --format=dir).
1216
 
 
1217
 
    Root may be the top directory for tar, tgz and tbz2 formats. If none
1218
 
    is given, the top directory will be the root name of the file."""
 
999
    given, exports to a directory (equivalent to --format=dir)."""
1219
1000
    # TODO: list known exporters
1220
1001
    takes_args = ['dest']
1221
 
    takes_options = ['revision', 'format', 'root']
1222
 
    def run(self, dest, revision=None, format=None, root=None):
1223
 
        import os.path
1224
 
        b = find_branch('.')
1225
 
        if revision is None:
1226
 
            rev_id = b.last_patch()
 
1002
    takes_options = ['revision', 'format']
 
1003
    def run(self, dest, revision=None, format='dir'):
 
1004
        b = Branch('.')
 
1005
        if revision == None:
 
1006
            rh = b.revision_history()[-1]
1227
1007
        else:
1228
 
            if len(revision) != 1:
1229
 
                raise BzrError('bzr export --revision takes exactly 1 argument')
1230
 
            revno, rev_id = b.get_revision_info(revision[0])
1231
 
        t = b.revision_tree(rev_id)
1232
 
        root, ext = os.path.splitext(dest)
1233
 
        if not format:
1234
 
            if ext in (".tar",):
1235
 
                format = "tar"
1236
 
            elif ext in (".gz", ".tgz"):
1237
 
                format = "tgz"
1238
 
            elif ext in (".bz2", ".tbz2"):
1239
 
                format = "tbz2"
1240
 
            else:
1241
 
                format = "dir"
1242
 
        t.export(dest, format, root)
 
1008
            rh = b.lookup_revision(int(revision))
 
1009
        t = b.revision_tree(rh)
 
1010
        t.export(dest, format)
1243
1011
 
1244
1012
 
1245
1013
class cmd_cat(Command):
1251
1019
    def run(self, filename, revision=None):
1252
1020
        if revision == None:
1253
1021
            raise BzrCommandError("bzr cat requires a revision number")
1254
 
        elif len(revision) != 1:
1255
 
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1256
 
        b = find_branch('.')
1257
 
        b.print_file(b.relpath(filename), revision[0])
 
1022
        b = Branch('.')
 
1023
        b.print_file(b.relpath(filename), int(revision))
1258
1024
 
1259
1025
 
1260
1026
class cmd_local_time_offset(Command):
1267
1033
 
1268
1034
class cmd_commit(Command):
1269
1035
    """Commit changes into a new revision.
1270
 
    
1271
 
    If no arguments are given, the entire tree is committed.
1272
1036
 
1273
1037
    If selected files are specified, only changes to those files are
1274
 
    committed.  If a directory is specified then the directory and everything 
1275
 
    within it is committed.
 
1038
    committed.  If a directory is specified then its contents are also
 
1039
    committed.
1276
1040
 
1277
1041
    A selected-file commit may fail in some cases where the committed
1278
1042
    tree would be invalid, such as trying to commit a file in a
1283
1047
    TODO: Strict commit that fails if there are unknown or deleted files.
1284
1048
    """
1285
1049
    takes_args = ['selected*']
1286
 
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
1050
    takes_options = ['message', 'file', 'verbose']
1287
1051
    aliases = ['ci', 'checkin']
1288
1052
 
1289
 
    # TODO: Give better message for -s, --summary, used by tla people
1290
 
    
1291
 
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1292
 
            unchanged=False):
1293
 
        from bzrlib.errors import PointlessCommit
1294
 
        from bzrlib.osutils import get_text_message
 
1053
    def run(self, message=None, file=None, verbose=True, selected_list=None):
 
1054
        from bzrlib.commit import commit
1295
1055
 
1296
1056
        ## Warning: shadows builtin file()
1297
1057
        if not message and not file:
1298
 
            # FIXME: Ugly; change status code to send to a provided function?
1299
 
            
1300
 
            import cStringIO
1301
 
            stdout = sys.stdout
1302
 
            catcher = cStringIO.StringIO()
1303
 
            sys.stdout = catcher
1304
 
            cmd_status({"file_list":selected_list}, {})
1305
 
            info = catcher.getvalue()
1306
 
            sys.stdout = stdout
1307
 
            message = get_text_message(info)
1308
 
            
1309
 
            if message is None:
1310
 
                raise BzrCommandError("please specify a commit message",
1311
 
                                      ["use either --message or --file"])
 
1058
            raise BzrCommandError("please specify a commit message",
 
1059
                                  ["use either --message or --file"])
1312
1060
        elif message and file:
1313
1061
            raise BzrCommandError("please specify either --message or --file")
1314
1062
        
1316
1064
            import codecs
1317
1065
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1318
1066
 
1319
 
        b = find_branch('.')
1320
 
        if selected_list:
1321
 
            selected_list = [b.relpath(s) for s in selected_list]
1322
 
            
1323
 
        try:
1324
 
            b.commit(message, verbose=verbose,
1325
 
                     specific_files=selected_list,
1326
 
                     allow_pointless=unchanged)
1327
 
        except PointlessCommit:
1328
 
            # FIXME: This should really happen before the file is read in;
1329
 
            # perhaps prepare the commit; get the message; then actually commit
1330
 
            raise BzrCommandError("no changes to commit",
1331
 
                                  ["use --unchanged to commit anyhow"])
 
1067
        b = Branch('.')
 
1068
        commit(b, message, verbose=verbose, specific_files=selected_list)
1332
1069
 
1333
1070
 
1334
1071
class cmd_check(Command):
1343
1080
    takes_args = ['dir?']
1344
1081
 
1345
1082
    def run(self, dir='.'):
1346
 
        from bzrlib.check import check
1347
 
 
1348
 
        check(find_branch(dir))
1349
 
 
1350
 
 
1351
 
class cmd_scan_cache(Command):
1352
 
    hidden = True
1353
 
    def run(self):
1354
 
        from bzrlib.hashcache import HashCache
1355
 
        import os
1356
 
 
1357
 
        c = HashCache('.')
1358
 
        c.read()
1359
 
        c.scan()
1360
 
            
1361
 
        print '%6d stats' % c.stat_count
1362
 
        print '%6d in hashcache' % len(c._cache)
1363
 
        print '%6d files removed from cache' % c.removed_count
1364
 
        print '%6d hashes updated' % c.update_count
1365
 
        print '%6d files changed too recently to cache' % c.danger_count
1366
 
 
1367
 
        if c.needs_write:
1368
 
            c.write()
1369
 
            
 
1083
        import bzrlib.check
 
1084
        bzrlib.check.check(Branch(dir))
 
1085
 
1370
1086
 
1371
1087
 
1372
1088
class cmd_upgrade(Command):
1379
1095
 
1380
1096
    def run(self, dir='.'):
1381
1097
        from bzrlib.upgrade import upgrade
1382
 
        upgrade(find_branch(dir))
 
1098
        upgrade(Branch(dir))
1383
1099
 
1384
1100
 
1385
1101
 
1388
1104
    takes_options = ['email']
1389
1105
    
1390
1106
    def run(self, email=False):
1391
 
        try:
1392
 
            b = bzrlib.branch.find_branch('.')
1393
 
        except:
1394
 
            b = None
1395
 
        
1396
1107
        if email:
1397
 
            print bzrlib.osutils.user_email(b)
 
1108
            print bzrlib.osutils.user_email()
1398
1109
        else:
1399
 
            print bzrlib.osutils.username(b)
 
1110
            print bzrlib.osutils.username()
1400
1111
 
1401
1112
 
1402
1113
class cmd_selftest(Command):
1403
1114
    """Run internal test suite"""
1404
1115
    hidden = True
1405
 
    takes_options = ['verbose']
1406
 
    def run(self, verbose=False):
1407
 
        import bzrlib.ui
 
1116
    def run(self):
1408
1117
        from bzrlib.selftest import selftest
1409
 
 
1410
 
        # we don't want progress meters from the tests to go to the
1411
 
        # real output.
1412
 
 
1413
 
        save_ui = bzrlib.ui.ui_factory
1414
 
        try:
1415
 
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1416
 
            return int(not selftest(verbose=verbose))
1417
 
        finally:
1418
 
            bzrlib.ui.ui_factory = save_ui
 
1118
        return int(not selftest())
1419
1119
 
1420
1120
 
1421
1121
class cmd_version(Command):
1453
1153
    ['..', -1]
1454
1154
    >>> parse_spec("../f/@35")
1455
1155
    ['../f', 35]
1456
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1457
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1458
1156
    """
1459
1157
    if spec is None:
1460
1158
        return [None, None]
1464
1162
        if parsed[1] == "":
1465
1163
            parsed[1] = -1
1466
1164
        else:
1467
 
            try:
1468
 
                parsed[1] = int(parsed[1])
1469
 
            except ValueError:
1470
 
                pass # We can allow stuff like ./@revid:blahblahblah
1471
 
            else:
1472
 
                assert parsed[1] >=0
 
1165
            parsed[1] = int(parsed[1])
 
1166
            assert parsed[1] >=0
1473
1167
    else:
1474
1168
        parsed = [spec, None]
1475
1169
    return parsed
1476
1170
 
1477
1171
 
1478
1172
 
1479
 
class cmd_find_merge_base(Command):
1480
 
    """Find and print a base revision for merging two branches.
1481
 
 
1482
 
    TODO: Options to specify revisions on either side, as if
1483
 
          merging only part of the history.
1484
 
    """
1485
 
    takes_args = ['branch', 'other']
1486
 
    hidden = True
1487
 
    
1488
 
    def run(self, branch, other):
1489
 
        branch1 = find_branch(branch)
1490
 
        branch2 = find_branch(other)
1491
 
 
1492
 
        base_revno, base_revid = branch1.common_ancestor(branch2)
1493
 
 
1494
 
        if base_revno is None:
1495
 
            raise bzrlib.errors.UnrelatedBranches()
1496
 
 
1497
 
        print 'merge base is revision %s' % base_revid
1498
 
        print ' r%-6d in %s' % (base_revno, branch)
1499
 
 
1500
 
        other_revno = branch2.revision_id_to_revno(base_revid)
1501
 
        
1502
 
        print ' r%-6d in %s' % (other_revno, other)
1503
 
 
1504
 
 
1505
 
 
1506
1173
class cmd_merge(Command):
1507
 
    """Perform a three-way merge.
1508
 
    
1509
 
    The branch is the branch you will merge from.  By default, it will merge
1510
 
    the latest revision.  If you specify a revision, that revision will be
1511
 
    merged.  If you specify two revisions, the first will be used as a BASE, 
1512
 
    and the second one as OTHER.  Revision numbers are always relative to the
1513
 
    specified branch.
1514
 
    
1515
 
    Examples:
1516
 
 
1517
 
    To merge the latest revision from bzr.dev
1518
 
    bzr merge ../bzr.dev
1519
 
 
1520
 
    To merge changes up to and including revision 82 from bzr.dev
1521
 
    bzr merge -r 82 ../bzr.dev
1522
 
 
1523
 
    To merge the changes introduced by 82, without previous changes:
1524
 
    bzr merge -r 81..82 ../bzr.dev
1525
 
    
 
1174
    """Perform a three-way merge of trees.
 
1175
    
 
1176
    The SPEC parameters are working tree or revision specifiers.  Working trees
 
1177
    are specified using standard paths or urls.  No component of a directory
 
1178
    path may begin with '@'.
 
1179
    
 
1180
    Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
 
1181
 
 
1182
    Revisions are specified using a dirname/@revno pair, where dirname is the
 
1183
    branch directory and revno is the revision within that branch.  If no revno
 
1184
    is specified, the latest revision is used.
 
1185
 
 
1186
    Revision examples: './@127', 'foo/@', '../@1'
 
1187
 
 
1188
    The OTHER_SPEC parameter is required.  If the BASE_SPEC parameter is
 
1189
    not supplied, the common ancestor of OTHER_SPEC the current branch is used
 
1190
    as the BASE.
 
1191
 
1526
1192
    merge refuses to run if there are any uncommitted changes, unless
1527
1193
    --force is given.
1528
1194
    """
1529
 
    takes_args = ['branch?']
1530
 
    takes_options = ['revision', 'force', 'merge-type']
 
1195
    takes_args = ['other_spec', 'base_spec?']
 
1196
    takes_options = ['force']
1531
1197
 
1532
 
    def run(self, branch='.', revision=None, force=False, 
1533
 
            merge_type=None):
 
1198
    def run(self, other_spec, base_spec=None, force=False):
1534
1199
        from bzrlib.merge import merge
1535
 
        from bzrlib.merge_core import ApplyMerge3
1536
 
        if merge_type is None:
1537
 
            merge_type = ApplyMerge3
1538
 
 
1539
 
        if revision is None or len(revision) < 1:
1540
 
            base = (None, None)
1541
 
            other = (branch, -1)
1542
 
        else:
1543
 
            if len(revision) == 1:
1544
 
                other = (branch, revision[0])
1545
 
                base = (None, None)
1546
 
            else:
1547
 
                assert len(revision) == 2
1548
 
                if None in revision:
1549
 
                    raise BzrCommandError(
1550
 
                        "Merge doesn't permit that revision specifier.")
1551
 
                base = (branch, revision[0])
1552
 
                other = (branch, revision[1])
1553
 
            
1554
 
        merge(other, base, check_clean=(not force), merge_type=merge_type)
 
1200
        merge(parse_spec(other_spec), parse_spec(base_spec),
 
1201
              check_clean=(not force))
1555
1202
 
1556
1203
 
1557
1204
class cmd_revert(Command):
1558
1205
    """Reverse all changes since the last commit.
1559
1206
 
1560
 
    Only versioned files are affected.  Specify filenames to revert only 
1561
 
    those files.  By default, any files that are changed will be backed up
1562
 
    first.  Backup files have a '~' appended to their name.
 
1207
    Only versioned files are affected.
 
1208
 
 
1209
    TODO: Store backups of any files that will be reverted, so
 
1210
          that the revert can be undone.          
1563
1211
    """
1564
 
    takes_options = ['revision', 'no-backup']
1565
 
    takes_args = ['file*']
1566
 
    aliases = ['merge-revert']
 
1212
    takes_options = ['revision']
1567
1213
 
1568
 
    def run(self, revision=None, no_backup=False, file_list=None):
 
1214
    def run(self, revision=-1):
1569
1215
        from bzrlib.merge import merge
1570
 
        if file_list is not None:
1571
 
            if len(file_list) == 0:
1572
 
                raise BzrCommandError("No files specified")
1573
 
        if revision is None:
1574
 
            revision = [-1]
1575
 
        elif len(revision) != 1:
1576
 
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1577
 
        merge(('.', revision[0]), parse_spec('.'),
 
1216
        merge(('.', revision), parse_spec('.'),
1578
1217
              check_clean=False,
1579
 
              ignore_zero=True,
1580
 
              backup_files=not no_backup,
1581
 
              file_list=file_list)
 
1218
              ignore_zero=True)
1582
1219
 
1583
1220
 
1584
1221
class cmd_assert_fail(Command):
1592
1229
    """Show help on a command or other topic.
1593
1230
 
1594
1231
    For a list of all available commands, say 'bzr help commands'."""
1595
 
    takes_options = ['long']
1596
1232
    takes_args = ['topic?']
1597
1233
    aliases = ['?']
1598
1234
    
1599
 
    def run(self, topic=None, long=False):
 
1235
    def run(self, topic=None):
1600
1236
        import help
1601
 
        if topic is None and long:
1602
 
            topic = "commands"
1603
1237
        help.help(topic)
1604
1238
 
1605
1239
 
1606
 
class cmd_shell_complete(Command):
1607
 
    """Show appropriate completions for context.
1608
 
 
1609
 
    For a list of all available commands, say 'bzr shell-complete'."""
1610
 
    takes_args = ['context?']
1611
 
    aliases = ['s-c']
1612
 
    hidden = True
1613
 
    
1614
 
    def run(self, context=None):
1615
 
        import shellcomplete
1616
 
        shellcomplete.shellcomplete(context)
1617
 
 
1618
 
 
1619
 
class cmd_missing(Command):
1620
 
    """What is missing in this branch relative to other branch.
1621
 
    """
1622
 
    takes_args = ['remote?']
1623
 
    aliases = ['mis', 'miss']
1624
 
    # We don't have to add quiet to the list, because 
1625
 
    # unknown options are parsed as booleans
1626
 
    takes_options = ['verbose', 'quiet']
1627
 
 
1628
 
    def run(self, remote=None, verbose=False, quiet=False):
1629
 
        from bzrlib.branch import find_branch, DivergedBranches
1630
 
        from bzrlib.errors import BzrCommandError
1631
 
        from bzrlib.missing import get_parent, show_missing
1632
 
 
1633
 
        if verbose and quiet:
1634
 
            raise BzrCommandError('Cannot pass both quiet and verbose')
1635
 
 
1636
 
        b = find_branch('.')
1637
 
        parent = get_parent(b)
1638
 
        if remote is None:
1639
 
            if parent is None:
1640
 
                raise BzrCommandError("No missing location known or specified.")
1641
 
            else:
1642
 
                if not quiet:
1643
 
                    print "Using last location: %s" % parent
1644
 
                remote = parent
1645
 
        elif parent is None:
1646
 
            # We only update x-pull if it did not exist, missing should not change the parent
1647
 
            b.controlfile('x-pull', 'wb').write(remote + '\n')
1648
 
        br_remote = find_branch(remote)
1649
 
 
1650
 
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1651
 
 
1652
 
 
1653
 
 
1654
 
class cmd_plugins(Command):
1655
 
    """List plugins"""
 
1240
class cmd_update_stat_cache(Command):
 
1241
    """Update stat-cache mapping inodes to SHA-1 hashes.
 
1242
 
 
1243
    For testing only."""
1656
1244
    hidden = True
1657
1245
    def run(self):
1658
 
        import bzrlib.plugin
1659
 
        from inspect import getdoc
1660
 
        from pprint import pprint
1661
 
        for plugin in bzrlib.plugin.all_plugins:
1662
 
            print plugin.__path__[0]
1663
 
            d = getdoc(plugin)
1664
 
            if d:
1665
 
                print '\t', d.split('\n')[0]
1666
 
 
1667
 
        #pprint(bzrlib.plugin.all_plugins)
 
1246
        import statcache
 
1247
        b = Branch('.')
 
1248
        statcache.update_cache(b.base, b.read_working_inventory())
1668
1249
 
1669
1250
 
1670
1251
 
1683
1264
    'no-recurse':             None,
1684
1265
    'profile':                None,
1685
1266
    'revision':               _parse_revision_str,
1686
 
    'short':                  None,
1687
1267
    'show-ids':               None,
1688
1268
    'timezone':               str,
1689
1269
    'verbose':                None,
1690
1270
    'version':                None,
1691
1271
    'email':                  None,
1692
 
    'unchanged':              None,
1693
1272
    'update':                 None,
1694
 
    'long':                   None,
1695
 
    'root':                   str,
1696
 
    'no-backup':              None,
1697
 
    'merge-type':             get_merge_type,
1698
1273
    }
1699
1274
 
1700
1275
SHORT_OPTIONS = {
1703
1278
    'm':                      'message',
1704
1279
    'r':                      'revision',
1705
1280
    'v':                      'verbose',
1706
 
    'l':                      'long',
1707
1281
}
1708
1282
 
1709
1283
 
1724
1298
    >>> parse_args('commit --message=biter'.split())
1725
1299
    (['commit'], {'message': u'biter'})
1726
1300
    >>> parse_args('log -r 500'.split())
1727
 
    (['log'], {'revision': [500]})
1728
 
    >>> parse_args('log -r500..600'.split())
 
1301
    (['log'], {'revision': 500})
 
1302
    >>> parse_args('log -r500:600'.split())
1729
1303
    (['log'], {'revision': [500, 600]})
1730
 
    >>> parse_args('log -vr500..600'.split())
 
1304
    >>> parse_args('log -vr500:600'.split())
1731
1305
    (['log'], {'verbose': True, 'revision': [500, 600]})
1732
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
1733
 
    (['log'], {'revision': ['v500', 600]})
 
1306
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
 
1307
    Traceback (most recent call last):
 
1308
    ...
 
1309
    ValueError: invalid literal for int(): v500
1734
1310
    """
1735
1311
    args = []
1736
1312
    opts = {}
1850
1426
    return argdict
1851
1427
 
1852
1428
 
 
1429
def _parse_master_args(argv):
 
1430
    """Parse the arguments that always go with the original command.
 
1431
    These are things like bzr --no-plugins, etc.
 
1432
 
 
1433
    There are now 2 types of option flags. Ones that come *before* the command,
 
1434
    and ones that come *after* the command.
 
1435
    Ones coming *before* the command are applied against all possible commands.
 
1436
    And are generally applied before plugins are loaded.
 
1437
 
 
1438
    The current list are:
 
1439
        --builtin   Allow plugins to load, but don't let them override builtin commands,
 
1440
                    they will still be allowed if they do not override a builtin.
 
1441
        --no-plugins    Don't load any plugins. This lets you get back to official source
 
1442
                        behavior.
 
1443
        --profile   Enable the hotspot profile before running the command.
 
1444
                    For backwards compatibility, this is also a non-master option.
 
1445
        --version   Spit out the version of bzr that is running and exit.
 
1446
                    This is also a non-master option.
 
1447
        --help      Run help and exit, also a non-master option (I think that should stay, though)
 
1448
 
 
1449
    >>> argv, opts = _parse_master_args(['bzr', '--test'])
 
1450
    Traceback (most recent call last):
 
1451
    ...
 
1452
    BzrCommandError: Invalid master option: 'test'
 
1453
    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
 
1454
    >>> print argv
 
1455
    ['command']
 
1456
    >>> print opts['version']
 
1457
    True
 
1458
    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
 
1459
    >>> print argv
 
1460
    ['command', '--more-options']
 
1461
    >>> print opts['profile']
 
1462
    True
 
1463
    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
 
1464
    >>> print argv
 
1465
    ['command']
 
1466
    >>> print opts['no-plugins']
 
1467
    True
 
1468
    >>> print opts['profile']
 
1469
    False
 
1470
    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
 
1471
    >>> print argv
 
1472
    ['command', '--profile']
 
1473
    >>> print opts['profile']
 
1474
    False
 
1475
    """
 
1476
    master_opts = {'builtin':False,
 
1477
        'no-plugins':False,
 
1478
        'version':False,
 
1479
        'profile':False,
 
1480
        'help':False
 
1481
    }
 
1482
 
 
1483
    # This is the point where we could hook into argv[0] to determine
 
1484
    # what front-end is supposed to be run
 
1485
    # For now, we are just ignoring it.
 
1486
    cmd_name = argv.pop(0)
 
1487
    for arg in argv[:]:
 
1488
        if arg[:2] != '--': # at the first non-option, we return the rest
 
1489
            break
 
1490
        arg = arg[2:] # Remove '--'
 
1491
        if arg not in master_opts:
 
1492
            # We could say that this is not an error, that we should
 
1493
            # just let it be handled by the main section instead
 
1494
            raise BzrCommandError('Invalid master option: %r' % arg)
 
1495
        argv.pop(0) # We are consuming this entry
 
1496
        master_opts[arg] = True
 
1497
    return argv, master_opts
 
1498
 
 
1499
 
1853
1500
 
1854
1501
def run_bzr(argv):
1855
1502
    """Execute a command.
1856
1503
 
1857
1504
    This is similar to main(), but without all the trappings for
1858
1505
    logging and error handling.  
1859
 
    
1860
 
    argv
1861
 
       The command-line arguments, without the program name from argv[0]
1862
 
    
1863
 
    Returns a command status or raises an exception.
1864
 
 
1865
 
    Special master options: these must come before the command because
1866
 
    they control how the command is interpreted.
1867
 
 
1868
 
    --no-plugins
1869
 
        Do not load plugin modules at all
1870
 
 
1871
 
    --builtin
1872
 
        Only use builtin commands.  (Plugins are still allowed to change
1873
 
        other behaviour.)
1874
 
 
1875
 
    --profile
1876
 
        Run under the Python profiler.
1877
1506
    """
1878
 
    
1879
1507
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1880
 
 
1881
 
    opt_profile = opt_no_plugins = opt_builtin = False
1882
 
 
1883
 
    # --no-plugins is handled specially at a very early stage. We need
1884
 
    # to load plugins before doing other command parsing so that they
1885
 
    # can override commands, but this needs to happen first.
1886
 
 
1887
 
    for a in argv[:]:
1888
 
        if a == '--profile':
1889
 
            opt_profile = True
1890
 
        elif a == '--no-plugins':
1891
 
            opt_no_plugins = True
1892
 
        elif a == '--builtin':
1893
 
            opt_builtin = True
1894
 
        else:
1895
 
            break
1896
 
        argv.remove(a)
1897
 
 
1898
 
    if not opt_no_plugins:
1899
 
        from bzrlib.plugin import load_plugins
1900
 
        load_plugins()
1901
 
 
1902
 
    args, opts = parse_args(argv)
1903
 
 
1904
 
    if 'help' in opts:
1905
 
        from bzrlib.help import help
1906
 
        if args:
1907
 
            help(args[0])
1908
 
        else:
1909
 
            help()
1910
 
        return 0            
1911
 
        
1912
 
    if 'version' in opts:
1913
 
        show_version()
1914
 
        return 0
1915
 
    
1916
 
    if not args:
1917
 
        from bzrlib.help import help
1918
 
        help(None)
1919
 
        return 0
1920
 
    
1921
 
    cmd = str(args.pop(0))
1922
 
 
1923
 
    canonical_cmd, cmd_class = \
1924
 
                   get_cmd_class(cmd, plugins_override=not opt_builtin)
 
1508
    
 
1509
    try:
 
1510
        # some options like --builtin and --no-plugins have special effects
 
1511
        argv, master_opts = _parse_master_args(argv)
 
1512
        if 'no-plugins' not in master_opts:
 
1513
            bzrlib.load_plugins()
 
1514
 
 
1515
        args, opts = parse_args(argv)
 
1516
 
 
1517
        if master_opts['help']:
 
1518
            from bzrlib.help import help
 
1519
            if argv:
 
1520
                help(argv[0])
 
1521
            else:
 
1522
                help()
 
1523
            return 0            
 
1524
            
 
1525
        if 'help' in opts:
 
1526
            from bzrlib.help import help
 
1527
            if args:
 
1528
                help(args[0])
 
1529
            else:
 
1530
                help()
 
1531
            return 0
 
1532
        elif 'version' in opts:
 
1533
            show_version()
 
1534
            return 0
 
1535
        elif args and args[0] == 'builtin':
 
1536
            include_plugins=False
 
1537
            args = args[1:]
 
1538
        cmd = str(args.pop(0))
 
1539
    except IndexError:
 
1540
        import help
 
1541
        help.help()
 
1542
        return 1
 
1543
          
 
1544
 
 
1545
    plugins_override = not (master_opts['builtin'])
 
1546
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
 
1547
 
 
1548
    profile = master_opts['profile']
 
1549
    # For backwards compatibility, I would rather stick with --profile being a
 
1550
    # master/global option
 
1551
    if 'profile' in opts:
 
1552
        profile = True
 
1553
        del opts['profile']
1925
1554
 
1926
1555
    # check options are reasonable
1927
1556
    allowed = cmd_class.takes_options
1936
1565
    for k, v in opts.items():
1937
1566
        cmdopts[k.replace('-', '_')] = v
1938
1567
 
1939
 
    if opt_profile:
 
1568
    if profile:
1940
1569
        import hotshot, tempfile
1941
1570
        pffileno, pfname = tempfile.mkstemp()
1942
1571
        try:
1961
1590
        return cmd_class(cmdopts, cmdargs).status 
1962
1591
 
1963
1592
 
 
1593
def _report_exception(summary, quiet=False):
 
1594
    import traceback
 
1595
    log_error('bzr: ' + summary)
 
1596
    bzrlib.trace.log_exception()
 
1597
 
 
1598
    if not quiet:
 
1599
        tb = sys.exc_info()[2]
 
1600
        exinfo = traceback.extract_tb(tb)
 
1601
        if exinfo:
 
1602
            sys.stderr.write('  at %s:%d in %s()\n' % exinfo[-1][:3])
 
1603
        sys.stderr.write('  see ~/.bzr.log for debug information\n')
 
1604
 
 
1605
 
 
1606
 
1964
1607
def main(argv):
1965
 
    import bzrlib.ui
 
1608
    import errno
1966
1609
    
1967
 
    bzrlib.trace.log_startup(argv)
1968
 
 
1969
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
1610
    bzrlib.open_tracefile(argv)
1970
1611
 
1971
1612
    try:
1972
1613
        try:
1973
 
            return run_bzr(argv[1:])
1974
 
        finally:
1975
 
            # do this here inside the exception wrappers to catch EPIPE
1976
 
            sys.stdout.flush()
1977
 
    except BzrCommandError, e:
1978
 
        # command line syntax error, etc
1979
 
        log_error(str(e))
1980
 
        return 1
1981
 
    except BzrError, e:
1982
 
        bzrlib.trace.log_exception()
1983
 
        return 1
1984
 
    except AssertionError, e:
1985
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
1986
 
        return 3
1987
 
    except KeyboardInterrupt, e:
1988
 
        bzrlib.trace.note('interrupted')
1989
 
        return 2
1990
 
    except Exception, e:
1991
 
        import errno
1992
 
        if (isinstance(e, IOError) 
1993
 
            and hasattr(e, 'errno')
1994
 
            and e.errno == errno.EPIPE):
1995
 
            bzrlib.trace.note('broken pipe')
1996
 
            return 2
1997
 
        else:
1998
 
            bzrlib.trace.log_exception()
1999
 
            return 2
 
1614
            try:
 
1615
                return run_bzr(argv)
 
1616
            finally:
 
1617
                # do this here inside the exception wrappers to catch EPIPE
 
1618
                sys.stdout.flush()
 
1619
        except BzrError, e:
 
1620
            quiet = isinstance(e, (BzrCommandError))
 
1621
            _report_exception('error: ' + e.args[0], quiet=quiet)
 
1622
            if len(e.args) > 1:
 
1623
                for h in e.args[1]:
 
1624
                    # some explanation or hints
 
1625
                    log_error('  ' + h)
 
1626
            return 1
 
1627
        except AssertionError, e:
 
1628
            msg = 'assertion failed'
 
1629
            if str(e):
 
1630
                msg += ': ' + str(e)
 
1631
            _report_exception(msg)
 
1632
            return 2
 
1633
        except KeyboardInterrupt, e:
 
1634
            _report_exception('interrupted', quiet=True)
 
1635
            return 2
 
1636
        except Exception, e:
 
1637
            quiet = False
 
1638
            if (isinstance(e, IOError) 
 
1639
                and hasattr(e, 'errno')
 
1640
                and e.errno == errno.EPIPE):
 
1641
                quiet = True
 
1642
                msg = 'broken pipe'
 
1643
            else:
 
1644
                msg = str(e).rstrip('\n')
 
1645
            _report_exception(msg, quiet)
 
1646
            return 2
 
1647
    finally:
 
1648
        bzrlib.trace.close_trace()
2000
1649
 
2001
1650
 
2002
1651
if __name__ == '__main__':