~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-07-29 12:29:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050729122927-d51c2cedc14dd5d5
doc

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import sys, os
20
20
 
21
21
import bzrlib
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
 
22
from bzrlib.trace import mutter, note, log_error, warning
 
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):
34
51
    assert cmd.startswith("cmd_")
35
52
    return cmd[4:].replace('_','-')
36
53
 
 
54
 
37
55
def _parse_revision_str(revstr):
38
 
    """This handles a revision string -> revno. 
39
 
 
40
 
    There are several possibilities:
41
 
 
42
 
        '234'       -> 234
43
 
        '234:345'   -> [234, 345]
44
 
        ':234'      -> [None, 234]
45
 
        '234:'      -> [234, None]
46
 
 
47
 
    In the future we will also support:
48
 
        'uuid:blah-blah-blah'   -> ?
49
 
        'hash:blahblahblah'     -> ?
50
 
        potentially:
51
 
        'tag:mytag'             -> ?
 
56
    """This handles a revision string -> revno.
 
57
 
 
58
    This always returns a list.  The list will have one element for 
 
59
 
 
60
    It supports integers directly, but everything else it
 
61
    defers for passing to Branch.get_revision_info()
 
62
 
 
63
    >>> _parse_revision_str('234')
 
64
    [234]
 
65
    >>> _parse_revision_str('234..567')
 
66
    [234, 567]
 
67
    >>> _parse_revision_str('..')
 
68
    [None, None]
 
69
    >>> _parse_revision_str('..234')
 
70
    [None, 234]
 
71
    >>> _parse_revision_str('234..')
 
72
    [234, None]
 
73
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
74
    [234, 456, 789]
 
75
    >>> _parse_revision_str('234....789') # Error?
 
76
    [234, None, 789]
 
77
    >>> _parse_revision_str('revid:test@other.com-234234')
 
78
    ['revid:test@other.com-234234']
 
79
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
80
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
 
81
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
82
    ['revid:test@other.com-234234', 23]
 
83
    >>> _parse_revision_str('date:2005-04-12')
 
84
    ['date:2005-04-12']
 
85
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
86
    ['date:2005-04-12 12:24:33']
 
87
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
88
    ['date:2005-04-12T12:24:33']
 
89
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
90
    ['date:2005-04-12,12:24:33']
 
91
    >>> _parse_revision_str('-5..23')
 
92
    [-5, 23]
 
93
    >>> _parse_revision_str('-5')
 
94
    [-5]
 
95
    >>> _parse_revision_str('123a')
 
96
    ['123a']
 
97
    >>> _parse_revision_str('abc')
 
98
    ['abc']
52
99
    """
53
 
    if revstr.find(':') != -1:
54
 
        revs = revstr.split(':')
55
 
        if len(revs) > 2:
56
 
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
57
 
 
58
 
        if not revs[0]:
59
 
            revs[0] = None
60
 
        else:
61
 
            revs[0] = int(revs[0])
62
 
 
63
 
        if not revs[1]:
64
 
            revs[1] = None
65
 
        else:
66
 
            revs[1] = int(revs[1])
67
 
    else:
68
 
        revs = int(revstr)
 
100
    import re
 
101
    old_format_re = re.compile('\d*:\d*')
 
102
    m = old_format_re.match(revstr)
 
103
    if m:
 
104
        warning('Colon separator for revision numbers is deprecated.'
 
105
                ' Use .. instead')
 
106
        revs = []
 
107
        for rev in revstr.split(':'):
 
108
            if rev:
 
109
                revs.append(int(rev))
 
110
            else:
 
111
                revs.append(None)
 
112
        return revs
 
113
    revs = []
 
114
    for x in revstr.split('..'):
 
115
        if not x:
 
116
            revs.append(None)
 
117
        else:
 
118
            try:
 
119
                revs.append(int(x))
 
120
            except ValueError:
 
121
                revs.append(x)
69
122
    return revs
70
123
 
71
 
def _find_plugins():
72
 
    """Find all python files which are plugins, and load their commands
73
 
    to add to the list of "all commands"
74
 
 
75
 
    The environment variable BZRPATH is considered a delimited set of
76
 
    paths to look through. Each entry is searched for *.py files.
77
 
    If a directory is found, it is also searched, but they are 
78
 
    not searched recursively. This allows you to revctl the plugins.
79
 
    
80
 
    Inside the plugin should be a series of cmd_* function, which inherit from
81
 
    the bzrlib.commands.Command class.
82
 
    """
83
 
    bzrpath = os.environ.get('BZRPLUGINPATH', '')
84
 
 
85
 
    plugin_cmds = {} 
86
 
    if not bzrpath:
87
 
        return plugin_cmds
88
 
    _platform_extensions = {
89
 
        'win32':'.pyd',
90
 
        'cygwin':'.dll',
91
 
        'darwin':'.dylib',
92
 
        'linux2':'.so'
93
 
        }
94
 
    if _platform_extensions.has_key(sys.platform):
95
 
        platform_extension = _platform_extensions[sys.platform]
96
 
    else:
97
 
        platform_extension = None
98
 
    for d in bzrpath.split(os.pathsep):
99
 
        plugin_names = {} # This should really be a set rather than a dict
100
 
        for f in os.listdir(d):
101
 
            if f.endswith('.py'):
102
 
                f = f[:-3]
103
 
            elif f.endswith('.pyc') or f.endswith('.pyo'):
104
 
                f = f[:-4]
105
 
            elif platform_extension and f.endswith(platform_extension):
106
 
                f = f[:-len(platform_extension)]
107
 
                if f.endswidth('module'):
108
 
                    f = f[:-len('module')]
109
 
            else:
110
 
                continue
111
 
            if not plugin_names.has_key(f):
112
 
                plugin_names[f] = True
113
 
 
114
 
        plugin_names = plugin_names.keys()
115
 
        plugin_names.sort()
116
 
        try:
117
 
            sys.path.insert(0, d)
118
 
            for name in plugin_names:
119
 
                try:
120
 
                    old_module = None
121
 
                    try:
122
 
                        if sys.modules.has_key(name):
123
 
                            old_module = sys.modules[name]
124
 
                            del sys.modules[name]
125
 
                        plugin = __import__(name, locals())
126
 
                        for k in dir(plugin):
127
 
                            if k.startswith('cmd_'):
128
 
                                k_unsquished = _unsquish_command_name(k)
129
 
                                if not plugin_cmds.has_key(k_unsquished):
130
 
                                    plugin_cmds[k_unsquished] = getattr(plugin, k)
131
 
                                else:
132
 
                                    log_error('Two plugins defined the same command: %r' % k)
133
 
                                    log_error('Not loading the one in %r in dir %r' % (name, d))
134
 
                    finally:
135
 
                        if old_module:
136
 
                            sys.modules[name] = old_module
137
 
                except ImportError, e:
138
 
                    log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
139
 
        finally:
140
 
            sys.path.pop(0)
141
 
    return plugin_cmds
142
 
 
143
 
def _get_cmd_dict(include_plugins=True):
 
124
 
 
125
 
 
126
def _get_cmd_dict(plugins_override=True):
144
127
    d = {}
145
128
    for k, v in globals().iteritems():
146
129
        if k.startswith("cmd_"):
147
130
            d[_unsquish_command_name(k)] = v
148
 
    if include_plugins:
149
 
        d.update(_find_plugins())
 
131
    # If we didn't load plugins, the plugin_cmds dict will be empty
 
132
    if plugins_override:
 
133
        d.update(plugin_cmds)
 
134
    else:
 
135
        d2 = plugin_cmds.copy()
 
136
        d2.update(d)
 
137
        d = d2
150
138
    return d
 
139
 
151
140
    
152
 
def get_all_cmds(include_plugins=True):
 
141
def get_all_cmds(plugins_override=True):
153
142
    """Return canonical name and class for all registered commands."""
154
 
    for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
 
143
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
155
144
        yield k,v
156
145
 
157
146
 
158
 
def get_cmd_class(cmd,include_plugins=True):
 
147
def get_cmd_class(cmd, plugins_override=True):
159
148
    """Return the canonical name and command class for a command.
160
149
    """
161
150
    cmd = str(cmd)                      # not unicode
162
151
 
163
152
    # first look up this command under the specified name
164
 
    cmds = _get_cmd_dict(include_plugins=include_plugins)
 
153
    cmds = _get_cmd_dict(plugins_override=plugins_override)
165
154
    try:
166
155
        return cmd, cmds[cmd]
167
156
    except KeyError:
211
200
        assert isinstance(arguments, dict)
212
201
        cmdargs = options.copy()
213
202
        cmdargs.update(arguments)
214
 
        assert self.__doc__ != Command.__doc__, \
215
 
               ("No help message set for %r" % self)
 
203
        if self.__doc__ == Command.__doc__:
 
204
            from warnings import warn
 
205
            warn("No help message set for %r" % self)
216
206
        self.status = self.run(**cmdargs)
 
207
        if self.status is None:
 
208
            self.status = 0
217
209
 
218
210
    
219
211
    def run(self):
231
223
class ExternalCommand(Command):
232
224
    """Class to wrap external commands.
233
225
 
234
 
    We cheat a little here, when get_cmd_class() calls us we actually give it back
235
 
    an object we construct that has the appropriate path, help, options etc for the
236
 
    specified command.
237
 
 
238
 
    When run_bzr() tries to instantiate that 'class' it gets caught by the __call__
239
 
    method, which we override to call the Command.__init__ method. That then calls
240
 
    our run method which is pretty straight forward.
241
 
 
242
 
    The only wrinkle is that we have to map bzr's dictionary of options and arguments
243
 
    back into command line options and arguments for the script.
 
226
    We cheat a little here, when get_cmd_class() calls us we actually
 
227
    give it back an object we construct that has the appropriate path,
 
228
    help, options etc for the specified command.
 
229
 
 
230
    When run_bzr() tries to instantiate that 'class' it gets caught by
 
231
    the __call__ method, which we override to call the Command.__init__
 
232
    method. That then calls our run method which is pretty straight
 
233
    forward.
 
234
 
 
235
    The only wrinkle is that we have to map bzr's dictionary of options
 
236
    and arguments back into command line options and arguments for the
 
237
    script.
244
238
    """
245
239
 
246
240
    def find_command(cls, cmd):
264
258
 
265
259
        for opt in self.takes_options:
266
260
            if not opt in OPTIONS:
267
 
                bailout("Unknown option '%s' returned by external command %s"
268
 
                    % (opt, path))
 
261
                raise BzrError("Unknown option '%s' returned by external command %s"
 
262
                               % (opt, path))
269
263
 
270
264
        # TODO: Is there any way to check takes_args is valid here?
271
265
        self.takes_args = pipe.readline().split()
272
266
 
273
267
        if pipe.close() is not None:
274
 
            bailout("Failed funning '%s --bzr-usage'" % path)
 
268
            raise BzrError("Failed funning '%s --bzr-usage'" % path)
275
269
 
276
270
        pipe = os.popen('%s --bzr-help' % path, 'r')
277
271
        self.__doc__ = pipe.read()
278
272
        if pipe.close() is not None:
279
 
            bailout("Failed funning '%s --bzr-help'" % path)
 
273
            raise BzrError("Failed funning '%s --bzr-help'" % path)
280
274
 
281
275
    def __call__(self, options, arguments):
282
276
        Command.__init__(self, options, arguments)
343
337
    directory is shown.  Otherwise, only the status of the specified
344
338
    files or directories is reported.  If a directory is given, status
345
339
    is reported for everything inside that directory.
 
340
 
 
341
    If a revision is specified, the changes since that revision are shown.
346
342
    """
347
343
    takes_args = ['file*']
348
 
    takes_options = ['all', 'show-ids']
 
344
    takes_options = ['all', 'show-ids', 'revision']
349
345
    aliases = ['st', 'stat']
350
346
    
351
347
    def run(self, all=False, show_ids=False, file_list=None):
352
348
        if file_list:
353
 
            b = Branch(file_list[0])
 
349
            b = find_branch(file_list[0])
354
350
            file_list = [b.relpath(x) for x in file_list]
355
351
            # special case: only one path was given and it's the root
356
352
            # of the branch
357
353
            if file_list == ['']:
358
354
                file_list = None
359
355
        else:
360
 
            b = Branch('.')
361
 
        import status
362
 
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
363
 
                           specific_files=file_list)
 
356
            b = find_branch('.')
 
357
            
 
358
        from bzrlib.status import show_status
 
359
        show_status(b, show_unchanged=all, show_ids=show_ids,
 
360
                    specific_files=file_list)
364
361
 
365
362
 
366
363
class cmd_cat_revision(Command):
370
367
    takes_args = ['revision_id']
371
368
    
372
369
    def run(self, revision_id):
373
 
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
 
370
        from bzrlib.xml import pack_xml
 
371
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
374
372
 
375
373
 
376
374
class cmd_revno(Command):
378
376
 
379
377
    This is equal to the number of revisions on this branch."""
380
378
    def run(self):
381
 
        print Branch('.').revno()
 
379
        print find_branch('.').revno()
 
380
 
 
381
class cmd_revision_info(Command):
 
382
    """Show revision number and revision id for a given revision identifier.
 
383
    """
 
384
    hidden = True
 
385
    takes_args = ['revision_info*']
 
386
    takes_options = ['revision']
 
387
    def run(self, revision=None, revision_info_list=None):
 
388
        from bzrlib.branch import find_branch
 
389
 
 
390
        revs = []
 
391
        if revision is not None:
 
392
            revs.extend(revision)
 
393
        if revision_info_list is not None:
 
394
            revs.extend(revision_info_list)
 
395
        if len(revs) == 0:
 
396
            raise BzrCommandError('You must supply a revision identifier')
 
397
 
 
398
        b = find_branch('.')
 
399
 
 
400
        for rev in revs:
 
401
            print '%4d %s' % b.get_revision_info(rev)
382
402
 
383
403
    
384
404
class cmd_add(Command):
394
414
    whether already versioned or not, are searched for files or
395
415
    subdirectories that are neither versioned or ignored, and these
396
416
    are added.  This search proceeds recursively into versioned
397
 
    directories.
 
417
    directories.  If no names are given '.' is assumed.
398
418
 
399
 
    Therefore simply saying 'bzr add .' will version all files that
 
419
    Therefore simply saying 'bzr add' will version all files that
400
420
    are currently unknown.
401
421
 
402
422
    TODO: Perhaps adding a file whose directly is not versioned should
403
423
    recursively add that parent, rather than giving an error?
404
424
    """
405
 
    takes_args = ['file+']
 
425
    takes_args = ['file*']
406
426
    takes_options = ['verbose', 'no-recurse']
407
427
    
408
428
    def run(self, file_list, verbose=False, no_recurse=False):
409
 
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
 
429
        from bzrlib.add import smart_add
 
430
        smart_add(file_list, verbose, not no_recurse)
 
431
 
 
432
 
 
433
 
 
434
class cmd_mkdir(Command):
 
435
    """Create a new versioned directory.
 
436
 
 
437
    This is equivalent to creating the directory and then adding it.
 
438
    """
 
439
    takes_args = ['dir+']
 
440
 
 
441
    def run(self, dir_list):
 
442
        b = None
 
443
        
 
444
        for d in dir_list:
 
445
            os.mkdir(d)
 
446
            if not b:
 
447
                b = find_branch(d)
 
448
            b.add([d], verbose=True)
410
449
 
411
450
 
412
451
class cmd_relpath(Command):
415
454
    hidden = True
416
455
    
417
456
    def run(self, filename):
418
 
        print Branch(filename).relpath(filename)
 
457
        print find_branch(filename).relpath(filename)
419
458
 
420
459
 
421
460
 
424
463
    takes_options = ['revision', 'show-ids']
425
464
    
426
465
    def run(self, revision=None, show_ids=False):
427
 
        b = Branch('.')
 
466
        b = find_branch('.')
428
467
        if revision == None:
429
468
            inv = b.read_working_inventory()
430
469
        else:
431
 
            inv = b.get_revision_inventory(b.lookup_revision(revision))
 
470
            if len(revision) > 1:
 
471
                raise BzrCommandError('bzr inventory --revision takes'
 
472
                    ' exactly one revision identifier')
 
473
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
432
474
 
433
475
        for path, entry in inv.entries():
434
476
            if show_ids:
447
489
    """
448
490
    takes_args = ['source$', 'dest']
449
491
    def run(self, source_list, dest):
450
 
        b = Branch('.')
 
492
        b = find_branch('.')
451
493
 
452
494
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
453
495
 
469
511
    takes_args = ['from_name', 'to_name']
470
512
    
471
513
    def run(self, from_name, to_name):
472
 
        b = Branch('.')
 
514
        b = find_branch('.')
473
515
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
474
516
 
475
517
 
494
536
 
495
537
    def run(self, location=None):
496
538
        from bzrlib.merge import merge
 
539
        import tempfile
 
540
        from shutil import rmtree
497
541
        import errno
498
542
        
499
 
        br_to = Branch('.')
 
543
        br_to = find_branch('.')
500
544
        stored_loc = None
501
545
        try:
502
546
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
503
547
        except IOError, e:
504
 
            if errno == errno.ENOENT:
 
548
            if e.errno != errno.ENOENT:
505
549
                raise
506
550
        if location is None:
507
 
            location = stored_loc
508
 
        if location is None:
509
 
            raise BzrCommandError("No pull location known or specified.")
510
 
        from branch import find_branch, DivergedBranches
 
551
            if stored_loc is None:
 
552
                raise BzrCommandError("No pull location known or specified.")
 
553
            else:
 
554
                print "Using last location: %s" % stored_loc
 
555
                location = stored_loc
 
556
        cache_root = tempfile.mkdtemp()
 
557
        from bzrlib.branch import DivergedBranches
511
558
        br_from = find_branch(location)
512
559
        location = pull_loc(br_from)
513
560
        old_revno = br_to.revno()
514
561
        try:
515
 
            br_to.update_revisions(br_from)
516
 
        except DivergedBranches:
517
 
            raise BzrCommandError("These branches have diverged.  Try merge.")
518
 
            
519
 
        merge(('.', -1), ('.', old_revno), check_clean=False)
520
 
        if location != stored_loc:
521
 
            br_to.controlfile("x-pull", "wb").write(location + "\n")
 
562
            from branch import find_cached_branch, DivergedBranches
 
563
            br_from = find_cached_branch(location, cache_root)
 
564
            location = pull_loc(br_from)
 
565
            old_revno = br_to.revno()
 
566
            try:
 
567
                br_to.update_revisions(br_from)
 
568
            except DivergedBranches:
 
569
                raise BzrCommandError("These branches have diverged."
 
570
                    "  Try merge.")
 
571
                
 
572
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
573
            if location != stored_loc:
 
574
                br_to.controlfile("x-pull", "wb").write(location + "\n")
 
575
        finally:
 
576
            rmtree(cache_root)
522
577
 
523
578
 
524
579
 
537
592
    def run(self, from_location, to_location=None, revision=None):
538
593
        import errno
539
594
        from bzrlib.merge import merge
540
 
        from branch import find_branch, DivergedBranches, NoSuchRevision
 
595
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
596
             find_cached_branch, Branch
541
597
        from shutil import rmtree
542
 
        try:
543
 
            br_from = find_branch(from_location)
544
 
        except OSError, e:
545
 
            if e.errno == errno.ENOENT:
546
 
                raise BzrCommandError('Source location "%s" does not exist.' %
547
 
                                      to_location)
548
 
            else:
549
 
                raise
550
 
 
551
 
        if to_location is None:
552
 
            to_location = os.path.basename(from_location.rstrip("/\\"))
553
 
 
554
 
        try:
555
 
            os.mkdir(to_location)
556
 
        except OSError, e:
557
 
            if e.errno == errno.EEXIST:
558
 
                raise BzrCommandError('Target directory "%s" already exists.' %
559
 
                                      to_location)
560
 
            if e.errno == errno.ENOENT:
561
 
                raise BzrCommandError('Parent of "%s" does not exist.' %
562
 
                                      to_location)
563
 
            else:
564
 
                raise
565
 
        br_to = Branch(to_location, init=True)
566
 
 
567
 
        try:
568
 
            br_to.update_revisions(br_from, stop_revision=revision)
569
 
        except NoSuchRevision:
570
 
            rmtree(to_location)
571
 
            msg = "The branch %s has no revision %d." % (from_location,
572
 
                                                         revision)
573
 
            raise BzrCommandError(msg)
574
 
        merge((to_location, -1), (to_location, 0), this_dir=to_location,
575
 
              check_clean=False)
576
 
        from_location = pull_loc(br_from)
577
 
        br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
598
        from meta_store import CachedStore
 
599
        import tempfile
 
600
        cache_root = tempfile.mkdtemp()
 
601
 
 
602
        if revision is None:
 
603
            revision = [None]
 
604
        elif len(revision) > 1:
 
605
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
 
606
 
 
607
        try:
 
608
            try:
 
609
                br_from = find_cached_branch(from_location, cache_root)
 
610
            except OSError, e:
 
611
                if e.errno == errno.ENOENT:
 
612
                    raise BzrCommandError('Source location "%s" does not'
 
613
                                          ' exist.' % to_location)
 
614
                else:
 
615
                    raise
 
616
 
 
617
            if to_location is None:
 
618
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
619
 
 
620
            try:
 
621
                os.mkdir(to_location)
 
622
            except OSError, e:
 
623
                if e.errno == errno.EEXIST:
 
624
                    raise BzrCommandError('Target directory "%s" already'
 
625
                                          ' exists.' % to_location)
 
626
                if e.errno == errno.ENOENT:
 
627
                    raise BzrCommandError('Parent of "%s" does not exist.' %
 
628
                                          to_location)
 
629
                else:
 
630
                    raise
 
631
            br_to = Branch(to_location, init=True)
 
632
 
 
633
            br_to.set_root_id(br_from.get_root_id())
 
634
 
 
635
            if revision:
 
636
                if revision[0] is None:
 
637
                    revno = br_from.revno()
 
638
                else:
 
639
                    revno, rev_id = br_from.get_revision_info(revision[0])
 
640
                try:
 
641
                    br_to.update_revisions(br_from, stop_revision=revno)
 
642
                except NoSuchRevision:
 
643
                    rmtree(to_location)
 
644
                    msg = "The branch %s has no revision %d." % (from_location,
 
645
                                                                 revno)
 
646
                    raise BzrCommandError(msg)
 
647
            
 
648
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
649
                  check_clean=False, ignore_zero=True)
 
650
            from_location = pull_loc(br_from)
 
651
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
652
        finally:
 
653
            rmtree(cache_root)
578
654
 
579
655
 
580
656
def pull_loc(branch):
597
673
    takes_args = ['dir?']
598
674
 
599
675
    def run(self, dir='.'):
600
 
        b = Branch(dir)
 
676
        b = find_branch(dir)
601
677
        old_inv = b.basis_tree().inventory
602
678
        new_inv = b.read_working_inventory()
603
679
 
614
690
    def run(self, branch=None):
615
691
        import info
616
692
 
617
 
        from branch import find_branch
618
693
        b = find_branch(branch)
619
694
        info.show_info(b)
620
695
 
629
704
    takes_options = ['verbose']
630
705
    
631
706
    def run(self, file_list, verbose=False):
632
 
        b = Branch(file_list[0])
 
707
        b = find_branch(file_list[0])
633
708
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
634
709
 
635
710
 
643
718
    hidden = True
644
719
    takes_args = ['filename']
645
720
    def run(self, filename):
646
 
        b = Branch(filename)
 
721
        b = find_branch(filename)
647
722
        i = b.inventory.path2id(b.relpath(filename))
648
723
        if i == None:
649
 
            bailout("%r is not a versioned file" % filename)
 
724
            raise BzrError("%r is not a versioned file" % filename)
650
725
        else:
651
726
            print i
652
727
 
659
734
    hidden = True
660
735
    takes_args = ['filename']
661
736
    def run(self, filename):
662
 
        b = Branch(filename)
 
737
        b = find_branch(filename)
663
738
        inv = b.inventory
664
739
        fid = inv.path2id(b.relpath(filename))
665
740
        if fid == None:
666
 
            bailout("%r is not a versioned file" % filename)
 
741
            raise BzrError("%r is not a versioned file" % filename)
667
742
        for fip in inv.get_idpath(fid):
668
743
            print fip
669
744
 
672
747
    """Display list of revision ids on this branch."""
673
748
    hidden = True
674
749
    def run(self):
675
 
        for patchid in Branch('.').revision_history():
 
750
        for patchid in find_branch('.').revision_history():
676
751
            print patchid
677
752
 
678
753
 
679
754
class cmd_directories(Command):
680
755
    """Display list of versioned directories in this branch."""
681
756
    def run(self):
682
 
        for name, ie in Branch('.').read_working_inventory().directories():
 
757
        for name, ie in find_branch('.').read_working_inventory().directories():
683
758
            if name == '':
684
759
                print '.'
685
760
            else:
700
775
        bzr commit -m 'imported project'
701
776
    """
702
777
    def run(self):
 
778
        from bzrlib.branch import Branch
703
779
        Branch('.', init=True)
704
780
 
705
781
 
733
809
 
734
810
    def run(self, revision=None, file_list=None, diff_options=None):
735
811
        from bzrlib.diff import show_diff
736
 
        from bzrlib import find_branch
737
812
 
738
813
        if file_list:
739
814
            b = find_branch(file_list[0])
742
817
                # just pointing to top-of-tree
743
818
                file_list = None
744
819
        else:
745
 
            b = Branch('.')
 
820
            b = find_branch('.')
 
821
 
 
822
        # TODO: Make show_diff support taking 2 arguments
 
823
        base_rev = None
 
824
        if revision is not None:
 
825
            if len(revision) != 1:
 
826
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
 
827
            base_rev = revision[0]
746
828
    
747
 
        show_diff(b, revision, specific_files=file_list,
 
829
        show_diff(b, base_rev, specific_files=file_list,
748
830
                  external_diff_options=diff_options)
749
831
 
750
832
 
757
839
    TODO: Show files deleted since a previous revision, or between two revisions.
758
840
    """
759
841
    def run(self, show_ids=False):
760
 
        b = Branch('.')
 
842
        b = find_branch('.')
761
843
        old = b.basis_tree()
762
844
        new = b.working_tree()
763
845
 
778
860
    """List files modified in working tree."""
779
861
    hidden = True
780
862
    def run(self):
781
 
        import statcache
782
 
        b = Branch('.')
783
 
        inv = b.read_working_inventory()
784
 
        sc = statcache.update_cache(b, inv)
785
 
        basis = b.basis_tree()
786
 
        basis_inv = basis.inventory
787
 
        
788
 
        # We used to do this through iter_entries(), but that's slow
789
 
        # when most of the files are unmodified, as is usually the
790
 
        # case.  So instead we iterate by inventory entry, and only
791
 
        # calculate paths as necessary.
792
 
 
793
 
        for file_id in basis_inv:
794
 
            cacheentry = sc.get(file_id)
795
 
            if not cacheentry:                 # deleted
796
 
                continue
797
 
            ie = basis_inv[file_id]
798
 
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
799
 
                path = inv.id2path(file_id)
800
 
                print path
 
863
        from bzrlib.diff import compare_trees
 
864
 
 
865
        b = find_branch('.')
 
866
        td = compare_trees(b.basis_tree(), b.working_tree())
 
867
 
 
868
        for path, id, kind in td.modified:
 
869
            print path
801
870
 
802
871
 
803
872
 
805
874
    """List files added in working tree."""
806
875
    hidden = True
807
876
    def run(self):
808
 
        b = Branch('.')
 
877
        b = find_branch('.')
809
878
        wt = b.working_tree()
810
879
        basis_inv = b.basis_tree().inventory
811
880
        inv = wt.inventory
827
896
    takes_args = ['filename?']
828
897
    def run(self, filename=None):
829
898
        """Print the branch root."""
830
 
        from branch import find_branch
831
899
        b = find_branch(filename)
832
900
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
833
901
 
839
907
    -r revision requests a specific revision, -r :end or -r begin: are
840
908
    also valid.
841
909
 
 
910
    --message allows you to give a regular expression, which will be evaluated
 
911
    so that only matching entries will be displayed.
 
912
 
842
913
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
843
914
  
844
915
    """
845
916
 
846
917
    takes_args = ['filename?']
847
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
 
918
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
848
919
    
849
920
    def run(self, filename=None, timezone='original',
850
921
            verbose=False,
851
922
            show_ids=False,
852
923
            forward=False,
853
 
            revision=None):
854
 
        from bzrlib import show_log, find_branch
 
924
            revision=None,
 
925
            message=None,
 
926
            long=False):
 
927
        from bzrlib.branch import find_branch
 
928
        from bzrlib.log import log_formatter, show_log
855
929
        import codecs
856
930
 
857
931
        direction = (forward and 'forward') or 'reverse'
867
941
            b = find_branch('.')
868
942
            file_id = None
869
943
 
870
 
        if revision == None:
871
 
            revision = [None, None]
872
 
        elif isinstance(revision, int):
873
 
            revision = [revision, revision]
 
944
        if revision is None:
 
945
            rev1 = None
 
946
            rev2 = None
 
947
        elif len(revision) == 1:
 
948
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
 
949
        elif len(revision) == 2:
 
950
            rev1 = b.get_revision_info(revision[0])[0]
 
951
            rev2 = b.get_revision_info(revision[1])[0]
874
952
        else:
875
 
            # pair of revisions?
876
 
            pass
877
 
            
878
 
        assert len(revision) == 2
 
953
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
954
 
 
955
        if rev1 == 0:
 
956
            rev1 = None
 
957
        if rev2 == 0:
 
958
            rev2 = None
879
959
 
880
960
        mutter('encoding log as %r' % bzrlib.user_encoding)
881
961
 
883
963
        # in e.g. the default C locale.
884
964
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
885
965
 
886
 
        show_log(b, file_id,
887
 
                 show_timezone=timezone,
 
966
        if long:
 
967
            log_format = 'long'
 
968
        else:
 
969
            log_format = 'short'
 
970
        lf = log_formatter(log_format,
 
971
                           show_ids=show_ids,
 
972
                           to_file=outf,
 
973
                           show_timezone=timezone)
 
974
 
 
975
        show_log(b,
 
976
                 lf,
 
977
                 file_id,
888
978
                 verbose=verbose,
889
 
                 show_ids=show_ids,
890
 
                 to_file=outf,
891
979
                 direction=direction,
892
 
                 start_revision=revision[0],
893
 
                 end_revision=revision[1])
 
980
                 start_revision=rev1,
 
981
                 end_revision=rev2,
 
982
                 search=message)
894
983
 
895
984
 
896
985
 
901
990
    hidden = True
902
991
    takes_args = ["filename"]
903
992
    def run(self, filename):
904
 
        b = Branch(filename)
 
993
        b = find_branch(filename)
905
994
        inv = b.read_working_inventory()
906
995
        file_id = inv.path2id(b.relpath(filename))
907
996
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
915
1004
    """
916
1005
    hidden = True
917
1006
    def run(self, revision=None, verbose=False):
918
 
        b = Branch('.')
 
1007
        b = find_branch('.')
919
1008
        if revision == None:
920
1009
            tree = b.working_tree()
921
1010
        else:
939
1028
class cmd_unknowns(Command):
940
1029
    """List unknown files."""
941
1030
    def run(self):
942
 
        for f in Branch('.').unknowns():
 
1031
        from bzrlib.osutils import quotefn
 
1032
        for f in find_branch('.').unknowns():
943
1033
            print quotefn(f)
944
1034
 
945
1035
 
967
1057
        from bzrlib.atomicfile import AtomicFile
968
1058
        import os.path
969
1059
 
970
 
        b = Branch('.')
 
1060
        b = find_branch('.')
971
1061
        ifn = b.abspath('.bzrignore')
972
1062
 
973
1063
        if os.path.exists(ifn):
1007
1097
 
1008
1098
    See also: bzr ignore"""
1009
1099
    def run(self):
1010
 
        tree = Branch('.').working_tree()
 
1100
        tree = find_branch('.').working_tree()
1011
1101
        for path, file_class, kind, file_id in tree.list_files():
1012
1102
            if file_class != 'I':
1013
1103
                continue
1031
1121
        except ValueError:
1032
1122
            raise BzrCommandError("not a valid revision-number: %r" % revno)
1033
1123
 
1034
 
        print Branch('.').lookup_revision(revno)
 
1124
        print find_branch('.').lookup_revision(revno)
1035
1125
 
1036
1126
 
1037
1127
class cmd_export(Command):
1040
1130
    If no revision is specified this exports the last committed revision.
1041
1131
 
1042
1132
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1043
 
    given, exports to a directory (equivalent to --format=dir)."""
 
1133
    given, try to find the format with the extension. If no extension
 
1134
    is found exports to a directory (equivalent to --format=dir).
 
1135
 
 
1136
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1137
    is given, the top directory will be the root name of the file."""
1044
1138
    # TODO: list known exporters
1045
1139
    takes_args = ['dest']
1046
 
    takes_options = ['revision', 'format']
1047
 
    def run(self, dest, revision=None, format='dir'):
1048
 
        b = Branch('.')
1049
 
        if revision == None:
1050
 
            rh = b.revision_history()[-1]
 
1140
    takes_options = ['revision', 'format', 'root']
 
1141
    def run(self, dest, revision=None, format=None, root=None):
 
1142
        import os.path
 
1143
        b = find_branch('.')
 
1144
        if revision is None:
 
1145
            rev_id = b.last_patch()
1051
1146
        else:
1052
 
            rh = b.lookup_revision(int(revision))
1053
 
        t = b.revision_tree(rh)
1054
 
        t.export(dest, format)
 
1147
            if len(revision) != 1:
 
1148
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
1149
            revno, rev_id = b.get_revision_info(revision[0])
 
1150
        t = b.revision_tree(rev_id)
 
1151
        root, ext = os.path.splitext(dest)
 
1152
        if not format:
 
1153
            if ext in (".tar",):
 
1154
                format = "tar"
 
1155
            elif ext in (".gz", ".tgz"):
 
1156
                format = "tgz"
 
1157
            elif ext in (".bz2", ".tbz2"):
 
1158
                format = "tbz2"
 
1159
            else:
 
1160
                format = "dir"
 
1161
        t.export(dest, format, root)
1055
1162
 
1056
1163
 
1057
1164
class cmd_cat(Command):
1063
1170
    def run(self, filename, revision=None):
1064
1171
        if revision == None:
1065
1172
            raise BzrCommandError("bzr cat requires a revision number")
1066
 
        b = Branch('.')
1067
 
        b.print_file(b.relpath(filename), int(revision))
 
1173
        elif len(revision) != 1:
 
1174
            raise BzrCommandError("bzr cat --revision takes exactly one number")
 
1175
        b = find_branch('.')
 
1176
        b.print_file(b.relpath(filename), revision[0])
1068
1177
 
1069
1178
 
1070
1179
class cmd_local_time_offset(Command):
1077
1186
 
1078
1187
class cmd_commit(Command):
1079
1188
    """Commit changes into a new revision.
 
1189
    
 
1190
    If no arguments are given, the entire tree is committed.
1080
1191
 
1081
1192
    If selected files are specified, only changes to those files are
1082
 
    committed.  If a directory is specified then its contents are also
1083
 
    committed.
 
1193
    committed.  If a directory is specified then the directory and everything 
 
1194
    within it is committed.
1084
1195
 
1085
1196
    A selected-file commit may fail in some cases where the committed
1086
1197
    tree would be invalid, such as trying to commit a file in a
1091
1202
    TODO: Strict commit that fails if there are unknown or deleted files.
1092
1203
    """
1093
1204
    takes_args = ['selected*']
1094
 
    takes_options = ['message', 'file', 'verbose']
 
1205
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1095
1206
    aliases = ['ci', 'checkin']
1096
1207
 
1097
 
    def run(self, message=None, file=None, verbose=True, selected_list=None):
1098
 
        from bzrlib.commit import commit
 
1208
    # TODO: Give better message for -s, --summary, used by tla people
 
1209
    
 
1210
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1211
            unchanged=False):
 
1212
        from bzrlib.errors import PointlessCommit
 
1213
        from bzrlib.osutils import get_text_message
1099
1214
 
1100
1215
        ## Warning: shadows builtin file()
1101
1216
        if not message and not file:
1102
 
            raise BzrCommandError("please specify a commit message",
1103
 
                                  ["use either --message or --file"])
 
1217
            # FIXME: Ugly; change status code to send to a provided function?
 
1218
            
 
1219
            import cStringIO
 
1220
            stdout = sys.stdout
 
1221
            catcher = cStringIO.StringIO()
 
1222
            sys.stdout = catcher
 
1223
            cmd_status({"file_list":selected_list}, {})
 
1224
            info = catcher.getvalue()
 
1225
            sys.stdout = stdout
 
1226
            message = get_text_message(info)
 
1227
            
 
1228
            if message is None:
 
1229
                raise BzrCommandError("please specify a commit message",
 
1230
                                      ["use either --message or --file"])
1104
1231
        elif message and file:
1105
1232
            raise BzrCommandError("please specify either --message or --file")
1106
1233
        
1108
1235
            import codecs
1109
1236
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1110
1237
 
1111
 
        b = Branch('.')
1112
 
        commit(b, message, verbose=verbose, specific_files=selected_list)
 
1238
        b = find_branch('.')
 
1239
        if selected_list:
 
1240
            selected_list = [b.relpath(s) for s in selected_list]
 
1241
            
 
1242
        try:
 
1243
            b.commit(message, verbose=verbose,
 
1244
                     specific_files=selected_list,
 
1245
                     allow_pointless=unchanged)
 
1246
        except PointlessCommit:
 
1247
            # FIXME: This should really happen before the file is read in;
 
1248
            # perhaps prepare the commit; get the message; then actually commit
 
1249
            raise BzrCommandError("no changes to commit",
 
1250
                                  ["use --unchanged to commit anyhow"])
1113
1251
 
1114
1252
 
1115
1253
class cmd_check(Command):
1122
1260
    to help ensure data consistency.
1123
1261
    """
1124
1262
    takes_args = ['dir?']
1125
 
    takes_options = ['update']
1126
 
 
1127
 
    def run(self, dir='.', update=False):
1128
 
        import bzrlib.check
1129
 
        bzrlib.check.check(Branch(dir), update=update)
 
1263
 
 
1264
    def run(self, dir='.'):
 
1265
        from bzrlib.check import check
 
1266
        check(find_branch(dir))
 
1267
 
 
1268
 
 
1269
 
 
1270
class cmd_scan_cache(Command):
 
1271
    hidden = True
 
1272
    def run(self):
 
1273
        from bzrlib.hashcache import HashCache
 
1274
        import os
 
1275
 
 
1276
        c = HashCache('.')
 
1277
        c.read()
 
1278
        c.scan()
 
1279
            
 
1280
        print '%6d stats' % c.stat_count
 
1281
        print '%6d in hashcache' % len(c._cache)
 
1282
        print '%6d files removed from cache' % c.removed_count
 
1283
        print '%6d hashes updated' % c.update_count
 
1284
        print '%6d files changed too recently to cache' % c.danger_count
 
1285
 
 
1286
        if c.needs_write:
 
1287
            c.write()
 
1288
            
 
1289
 
 
1290
 
 
1291
class cmd_upgrade(Command):
 
1292
    """Upgrade branch storage to current format.
 
1293
 
 
1294
    This should normally be used only after the check command tells
 
1295
    you to run it.
 
1296
    """
 
1297
    takes_args = ['dir?']
 
1298
 
 
1299
    def run(self, dir='.'):
 
1300
        from bzrlib.upgrade import upgrade
 
1301
        upgrade(find_branch(dir))
1130
1302
 
1131
1303
 
1132
1304
 
1144
1316
class cmd_selftest(Command):
1145
1317
    """Run internal test suite"""
1146
1318
    hidden = True
1147
 
    def run(self):
 
1319
    takes_options = ['verbose']
 
1320
    def run(self, verbose=False):
1148
1321
        from bzrlib.selftest import selftest
1149
 
        if selftest():
1150
 
            return 0
1151
 
        else:
1152
 
            return 1
1153
 
 
 
1322
        return int(not selftest(verbose=verbose))
1154
1323
 
1155
1324
 
1156
1325
class cmd_version(Command):
1188
1357
    ['..', -1]
1189
1358
    >>> parse_spec("../f/@35")
1190
1359
    ['../f', 35]
 
1360
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
1361
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1191
1362
    """
1192
1363
    if spec is None:
1193
1364
        return [None, None]
1197
1368
        if parsed[1] == "":
1198
1369
            parsed[1] = -1
1199
1370
        else:
1200
 
            parsed[1] = int(parsed[1])
1201
 
            assert parsed[1] >=0
 
1371
            try:
 
1372
                parsed[1] = int(parsed[1])
 
1373
            except ValueError:
 
1374
                pass # We can allow stuff like ./@revid:blahblahblah
 
1375
            else:
 
1376
                assert parsed[1] >=0
1202
1377
    else:
1203
1378
        parsed = [spec, None]
1204
1379
    return parsed
1236
1411
              check_clean=(not force))
1237
1412
 
1238
1413
 
 
1414
 
1239
1415
class cmd_revert(Command):
 
1416
    """Restore selected files from a previous revision.
 
1417
    """
 
1418
    takes_args = ['file+']
 
1419
    def run(self, file_list):
 
1420
        from bzrlib.branch import find_branch
 
1421
        
 
1422
        if not file_list:
 
1423
            file_list = ['.']
 
1424
            
 
1425
        b = find_branch(file_list[0])
 
1426
 
 
1427
        b.revert([b.relpath(f) for f in file_list])
 
1428
 
 
1429
 
 
1430
class cmd_merge_revert(Command):
1240
1431
    """Reverse all changes since the last commit.
1241
1432
 
1242
1433
    Only versioned files are affected.
1246
1437
    """
1247
1438
    takes_options = ['revision']
1248
1439
 
1249
 
    def run(self, revision=-1):
 
1440
    def run(self, revision=None):
1250
1441
        from bzrlib.merge import merge
1251
 
        merge(('.', revision), parse_spec('.'),
 
1442
        if revision is None:
 
1443
            revision = [-1]
 
1444
        elif len(revision) != 1:
 
1445
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
 
1446
        merge(('.', revision[0]), parse_spec('.'),
1252
1447
              check_clean=False,
1253
1448
              ignore_zero=True)
1254
1449
 
1272
1467
        help.help(topic)
1273
1468
 
1274
1469
 
1275
 
class cmd_update_stat_cache(Command):
1276
 
    """Update stat-cache mapping inodes to SHA-1 hashes.
1277
 
 
1278
 
    For testing only."""
 
1470
 
 
1471
 
 
1472
class cmd_plugins(Command):
 
1473
    """List plugins"""
1279
1474
    hidden = True
1280
1475
    def run(self):
1281
 
        import statcache
1282
 
        b = Branch('.')
1283
 
        statcache.update_cache(b.base, b.read_working_inventory())
 
1476
        import bzrlib.plugin
 
1477
        from inspect import getdoc
 
1478
        from pprint import pprint
 
1479
        for plugin in bzrlib.plugin.all_plugins:
 
1480
            print plugin.__path__[0]
 
1481
            d = getdoc(plugin)
 
1482
            if d:
 
1483
                print '\t', d.split('\n')[0]
 
1484
 
 
1485
        #pprint(bzrlib.plugin.all_plugins)
1284
1486
 
1285
1487
 
1286
1488
 
1304
1506
    'verbose':                None,
1305
1507
    'version':                None,
1306
1508
    'email':                  None,
 
1509
    'unchanged':              None,
1307
1510
    'update':                 None,
 
1511
    'long':                   None,
 
1512
    'root':                   str,
1308
1513
    }
1309
1514
 
1310
1515
SHORT_OPTIONS = {
1313
1518
    'm':                      'message',
1314
1519
    'r':                      'revision',
1315
1520
    'v':                      'verbose',
 
1521
    'l':                      'long',
1316
1522
}
1317
1523
 
1318
1524
 
1333
1539
    >>> parse_args('commit --message=biter'.split())
1334
1540
    (['commit'], {'message': u'biter'})
1335
1541
    >>> parse_args('log -r 500'.split())
1336
 
    (['log'], {'revision': 500})
1337
 
    >>> parse_args('log -r500:600'.split())
 
1542
    (['log'], {'revision': [500]})
 
1543
    >>> parse_args('log -r500..600'.split())
1338
1544
    (['log'], {'revision': [500, 600]})
1339
 
    >>> parse_args('log -vr500:600'.split())
 
1545
    >>> parse_args('log -vr500..600'.split())
1340
1546
    (['log'], {'verbose': True, 'revision': [500, 600]})
1341
 
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
1342
 
    Traceback (most recent call last):
1343
 
    ...
1344
 
    ValueError: invalid literal for int(): v500
 
1547
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
1548
    (['log'], {'revision': ['v500', 600]})
1345
1549
    """
1346
1550
    args = []
1347
1551
    opts = {}
1361
1565
                else:
1362
1566
                    optname = a[2:]
1363
1567
                if optname not in OPTIONS:
1364
 
                    bailout('unknown long option %r' % a)
 
1568
                    raise BzrError('unknown long option %r' % a)
1365
1569
            else:
1366
1570
                shortopt = a[1:]
1367
1571
                if shortopt in SHORT_OPTIONS:
1375
1579
                    if shortopt not in SHORT_OPTIONS:
1376
1580
                        # We didn't find the multi-character name, and we
1377
1581
                        # didn't find the single char name
1378
 
                        bailout('unknown short option %r' % a)
 
1582
                        raise BzrError('unknown short option %r' % a)
1379
1583
                    optname = SHORT_OPTIONS[shortopt]
1380
1584
 
1381
1585
                    if a[2:]:
1395
1599
            
1396
1600
            if optname in opts:
1397
1601
                # XXX: Do we ever want to support this, e.g. for -r?
1398
 
                bailout('repeated option %r' % a)
 
1602
                raise BzrError('repeated option %r' % a)
1399
1603
                
1400
1604
            optargfn = OPTIONS[optname]
1401
1605
            if optargfn:
1402
1606
                if optarg == None:
1403
1607
                    if not argv:
1404
 
                        bailout('option %r needs an argument' % a)
 
1608
                        raise BzrError('option %r needs an argument' % a)
1405
1609
                    else:
1406
1610
                        optarg = argv.pop(0)
1407
1611
                opts[optname] = optargfn(optarg)
1408
1612
            else:
1409
1613
                if optarg != None:
1410
 
                    bailout('option %r takes no argument' % optname)
 
1614
                    raise BzrError('option %r takes no argument' % optname)
1411
1615
                opts[optname] = True
1412
1616
        else:
1413
1617
            args.append(a)
1467
1671
 
1468
1672
    This is similar to main(), but without all the trappings for
1469
1673
    logging and error handling.  
 
1674
    
 
1675
    argv
 
1676
       The command-line arguments, without the program name from argv[0]
 
1677
    
 
1678
    Returns a command status or raises an exception.
 
1679
 
 
1680
    Special master options: these must come before the command because
 
1681
    they control how the command is interpreted.
 
1682
 
 
1683
    --no-plugins
 
1684
        Do not load plugin modules at all
 
1685
 
 
1686
    --builtin
 
1687
        Only use builtin commands.  (Plugins are still allowed to change
 
1688
        other behaviour.)
 
1689
 
 
1690
    --profile
 
1691
        Run under the Python profiler.
1470
1692
    """
 
1693
    
1471
1694
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
1695
 
 
1696
    opt_profile = opt_no_plugins = opt_builtin = False
 
1697
 
 
1698
    # --no-plugins is handled specially at a very early stage. We need
 
1699
    # to load plugins before doing other command parsing so that they
 
1700
    # can override commands, but this needs to happen first.
 
1701
 
 
1702
    for a in argv[:]:
 
1703
        if a == '--profile':
 
1704
            opt_profile = True
 
1705
        elif a == '--no-plugins':
 
1706
            opt_no_plugins = True
 
1707
        elif a == '--builtin':
 
1708
            opt_builtin = True
 
1709
        else:
 
1710
            break
 
1711
        argv.remove(a)
 
1712
 
 
1713
    if not opt_no_plugins:
 
1714
        from bzrlib.plugin import load_plugins
 
1715
        load_plugins()
 
1716
 
 
1717
    args, opts = parse_args(argv)
 
1718
 
 
1719
    if 'help' in opts:
 
1720
        from bzrlib.help import help
 
1721
        if args:
 
1722
            help(args[0])
 
1723
        else:
 
1724
            help()
 
1725
        return 0            
 
1726
        
 
1727
    if 'version' in opts:
 
1728
        show_version()
 
1729
        return 0
1472
1730
    
1473
 
    include_plugins=True
1474
 
    try:
1475
 
        args, opts = parse_args(argv[1:])
1476
 
        if 'help' in opts:
1477
 
            import help
1478
 
            if args:
1479
 
                help.help(args[0])
1480
 
            else:
1481
 
                help.help()
1482
 
            return 0
1483
 
        elif 'version' in opts:
1484
 
            show_version()
1485
 
            return 0
1486
 
        elif args and args[0] == 'builtin':
1487
 
            include_plugins=False
1488
 
            args = args[1:]
1489
 
        cmd = str(args.pop(0))
1490
 
    except IndexError:
1491
 
        import help
1492
 
        help.help()
 
1731
    if not args:
 
1732
        print >>sys.stderr, "please try 'bzr help' for help"
1493
1733
        return 1
1494
 
          
1495
 
 
1496
 
    canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
1497
 
 
1498
 
    # global option
1499
 
    if 'profile' in opts:
1500
 
        profile = True
1501
 
        del opts['profile']
1502
 
    else:
1503
 
        profile = False
 
1734
    
 
1735
    cmd = str(args.pop(0))
 
1736
 
 
1737
    canonical_cmd, cmd_class = \
 
1738
                   get_cmd_class(cmd, plugins_override=not opt_builtin)
1504
1739
 
1505
1740
    # check options are reasonable
1506
1741
    allowed = cmd_class.takes_options
1515
1750
    for k, v in opts.items():
1516
1751
        cmdopts[k.replace('-', '_')] = v
1517
1752
 
1518
 
    if profile:
 
1753
    if opt_profile:
1519
1754
        import hotshot, tempfile
1520
1755
        pffileno, pfname = tempfile.mkstemp()
1521
1756
        try:
1555
1790
 
1556
1791
 
1557
1792
def main(argv):
1558
 
    import errno
1559
1793
    
1560
 
    bzrlib.open_tracefile(argv)
 
1794
    bzrlib.trace.open_tracefile(argv)
1561
1795
 
1562
1796
    try:
1563
1797
        try:
1564
1798
            try:
1565
 
                return run_bzr(argv)
 
1799
                return run_bzr(argv[1:])
1566
1800
            finally:
1567
1801
                # do this here inside the exception wrappers to catch EPIPE
1568
1802
                sys.stdout.flush()
1584
1818
            _report_exception('interrupted', quiet=True)
1585
1819
            return 2
1586
1820
        except Exception, e:
 
1821
            import errno
1587
1822
            quiet = False
1588
1823
            if (isinstance(e, IOError) 
1589
1824
                and hasattr(e, 'errno')