14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Bazaar-NG -- a free distributed version-control tool
20
**WARNING: THIS IS AN UNSTABLE DEVELOPMENT VERSION**
22
* Metadata format is not stable yet -- you may need to
23
discard history in the future.
25
* Many commands unimplemented or partially implemented.
27
* Space-inefficient storage.
29
* No merge operators yet.
36
Show software version/licence/non-warranty.
38
Start versioning the current directory
42
Show revision history.
45
bzr move FROM... DESTDIR
46
Move one or more files to a different directory.
48
Show changes from last revision to working copy.
49
bzr commit -m 'MESSAGE'
50
Store current state as new revision.
51
bzr export [-r REVNO] DESTINATION
52
Export the branch state at a previous version.
54
Show summary of pending changes.
56
Make a file not versioned.
58
Show statistics about this branch.
60
Verify history is stored safely.
61
(for more type 'bzr help commands')
67
import sys, os, time, types, shutil, tempfile, fnmatch, difflib, os.path
69
from pprint import pprint
74
from bzrlib.store import ImmutableStore
22
75
from bzrlib.trace import mutter, note, log_error
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
76
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
77
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
78
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
79
from bzrlib.revision import Revision
80
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
29
def _squish_command_name(cmd):
30
return 'cmd_' + cmd.replace('-', '_')
33
def _unsquish_command_name(cmd):
34
assert cmd.startswith("cmd_")
35
return cmd[4:].replace('_','-')
37
def _parse_revision_str(revstr):
38
"""This handles a revision string -> revno.
40
There are several possibilities:
43
'234:345' -> [234, 345]
47
In the future we will also support:
48
'uuid:blah-blah-blah' -> ?
49
'hash:blahblahblah' -> ?
53
if revstr.find(':') != -1:
54
revs = revstr.split(':')
56
raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
61
revs[0] = int(revs[0])
66
revs[1] = int(revs[1])
72
"""Find all python files which are plugins, and load their commands
73
to add to the list of "all commands"
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.
80
Inside the plugin should be a series of cmd_* function, which inherit from
81
the bzrlib.commands.Command class.
83
bzrpath = os.environ.get('BZRPLUGINPATH', '')
88
_platform_extensions = {
94
if _platform_extensions.has_key(sys.platform):
95
platform_extension = _platform_extensions[sys.platform]
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'):
103
elif f.endswith('.pyc') or f.endswith('.pyo'):
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')]
111
if not plugin_names.has_key(f):
112
plugin_names[f] = True
114
plugin_names = plugin_names.keys()
117
sys.path.insert(0, d)
118
for name in plugin_names:
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)
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))
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))
143
def _get_cmd_dict(include_plugins=True):
145
for k, v in globals().iteritems():
146
if k.startswith("cmd_"):
147
d[_unsquish_command_name(k)] = v
149
d.update(_find_plugins())
152
def get_all_cmds(include_plugins=True):
153
"""Return canonical name and class for all registered commands."""
154
for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
158
def get_cmd_class(cmd,include_plugins=True):
159
"""Return the canonical name and command class for a command.
161
cmd = str(cmd) # not unicode
163
# first look up this command under the specified name
164
cmds = _get_cmd_dict(include_plugins=include_plugins)
83
BZR_DIFF_FORMAT = "## Bazaar-NG diff, format 0 ##\n"
84
BZR_PATCHNAME_FORMAT = 'cset:sha1:%s'
86
## standard representation
87
NONE_STRING = '(none)'
101
def get_cmd_class(cmd):
104
cmd = CMD_ALIASES.get(cmd, cmd)
166
return cmd, cmds[cmd]
107
cmd_class = globals()['cmd_' + cmd.replace('-', '_')]
170
# look for any command which claims this as an alias
171
for cmdname, cmdclass in cmds.iteritems():
172
if cmd in cmdclass.aliases:
173
return cmdname, cmdclass
175
cmdclass = ExternalCommand.find_command(cmd)
179
raise BzrCommandError("unknown command %r" % cmd)
182
class Command(object):
109
raise BzrError("unknown command %r" % cmd)
111
return cmd, cmd_class
183
116
"""Base class for commands.
185
118
The docstring for an actual command should give a single-line
222
155
This is invoked with the options and arguments bound to
223
156
keyword parameters.
225
Return 0 or None if the command was successful, or a shell
158
Return True if the command was successful, False if not.
231
class ExternalCommand(Command):
232
"""Class to wrap external commands.
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
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.
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.
246
def find_command(cls, cmd):
248
bzrpath = os.environ.get('BZRPATH', '')
250
for dir in bzrpath.split(os.pathsep):
251
path = os.path.join(dir, cmd)
252
if os.path.isfile(path):
253
return ExternalCommand(path)
257
find_command = classmethod(find_command)
259
def __init__(self, path):
262
pipe = os.popen('%s --bzr-usage' % path, 'r')
263
self.takes_options = pipe.readline().split()
265
for opt in self.takes_options:
266
if not opt in OPTIONS:
267
raise BzrError("Unknown option '%s' returned by external command %s"
270
# TODO: Is there any way to check takes_args is valid here?
271
self.takes_args = pipe.readline().split()
273
if pipe.close() is not None:
274
raise BzrError("Failed funning '%s --bzr-usage'" % path)
276
pipe = os.popen('%s --bzr-help' % path, 'r')
277
self.__doc__ = pipe.read()
278
if pipe.close() is not None:
279
raise BzrError("Failed funning '%s --bzr-help'" % path)
281
def __call__(self, options, arguments):
282
Command.__init__(self, options, arguments)
285
def run(self, **kargs):
292
optname = name.replace('_','-')
294
if OPTIONS.has_key(optname):
296
opts.append('--%s' % optname)
297
if value is not None and value is not True:
298
opts.append(str(value))
300
# it's an arg, or arg list
301
if type(value) is not list:
307
self.status = os.spawnv(os.P_WAIT, self.path, [self.path] + opts + args)
311
164
class cmd_status(Command):
312
165
"""Display status summary.
314
This reports on versioned and unknown files, reporting them
315
grouped by state. Possible states are:
318
Versioned in the working copy but not in the previous revision.
321
Versioned in the previous revision but removed or deleted
325
Path of this file changed from the previous revision;
326
the text may also have changed. This includes files whose
327
parent directory was renamed.
330
Text has changed since the previous revision.
333
Nothing about this file has changed since the previous revision.
334
Only shown with --all.
337
Not versioned and not matching an ignore pattern.
339
To see ignored files use 'bzr ignored'. For details in the
340
changes to file texts, use 'bzr diff'.
342
If no arguments are specified, the status of the entire working
343
directory is shown. Otherwise, only the status of the specified
344
files or directories is reported. If a directory is given, status
345
is reported for everything inside that directory.
167
For each file there is a single line giving its file state and name.
168
The name is that in the current revision unless it is deleted or
169
missing, in which case the old name is shown.
347
takes_args = ['file*']
348
takes_options = ['all', 'show-ids']
349
aliases = ['st', 'stat']
171
takes_options = ['all']
351
def run(self, all=False, show_ids=False, file_list=None):
353
b = Branch(file_list[0])
354
file_list = [b.relpath(x) for x in file_list]
355
# special case: only one path was given and it's the root
357
if file_list == ['']:
362
status.show_status(b, show_unchanged=all, show_ids=show_ids,
363
specific_files=file_list)
173
def run(self, all=False):
174
#import bzrlib.status
175
#bzrlib.status.tree_status(Branch('.'))
176
Branch('.').show_status(show_all=all)
366
179
class cmd_cat_revision(Command):
403
216
recursively add that parent, rather than giving an error?
405
218
takes_args = ['file+']
406
takes_options = ['verbose', 'no-recurse']
219
takes_options = ['verbose']
408
def run(self, file_list, verbose=False, no_recurse=False):
409
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
412
class cmd_relpath(Command):
221
def run(self, file_list, verbose=False):
222
bzrlib.add.smart_add(file_list, verbose)
225
def Relpath(Command):
413
226
"""Show path of a file relative to root"""
414
takes_args = ['filename']
227
takes_args = ('filename')
417
def run(self, filename):
418
print Branch(filename).relpath(filename)
230
print Branch(self.args['filename']).relpath(filename)
422
234
class cmd_inventory(Command):
423
235
"""Show inventory of the current working copy or a revision."""
424
takes_options = ['revision', 'show-ids']
236
takes_options = ['revision']
426
def run(self, revision=None, show_ids=False):
238
def run(self, revision=None):
428
240
if revision == None:
429
241
inv = b.read_working_inventory()
431
243
inv = b.get_revision_inventory(b.lookup_revision(revision))
433
for path, entry in inv.entries():
435
print '%-50s %s' % (path, entry.file_id)
245
for path, entry in inv.iter_entries():
246
print '%-50s %s' % (entry.file_id, path)
440
249
class cmd_move(Command):
479
class cmd_pull(Command):
480
"""Pull any changes from another branch into the current one.
482
If the location is omitted, the last-used location will be used.
483
Both the revision history and the working directory will be
486
This command only works on branches that have not diverged. Branches are
487
considered diverged if both branches have had commits without first
488
pulling from the other.
490
If branches have diverged, you can use 'bzr merge' to pull the text changes
491
from one into the other.
493
takes_args = ['location?']
495
def run(self, location=None):
496
from bzrlib.merge import merge
502
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
504
if errno == errno.ENOENT:
507
location = stored_loc
509
raise BzrCommandError("No pull location known or specified.")
510
from branch import find_branch, DivergedBranches
511
br_from = find_branch(location)
512
location = pull_loc(br_from)
513
old_revno = br_to.revno()
515
br_to.update_revisions(br_from)
516
except DivergedBranches:
517
raise BzrCommandError("These branches have diverged. Try merge.")
519
merge(('.', -1), ('.', old_revno), check_clean=False)
520
if location != stored_loc:
521
br_to.controlfile("x-pull", "wb").write(location + "\n")
525
class cmd_branch(Command):
526
"""Create a new copy of a branch.
528
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
529
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
531
To retrieve the branch as of a particular revision, supply the --revision
532
parameter, as in "branch foo/bar -r 5".
534
takes_args = ['from_location', 'to_location?']
535
takes_options = ['revision']
537
def run(self, from_location, to_location=None, revision=None):
539
from bzrlib.merge import merge
540
from branch import find_branch, DivergedBranches, NoSuchRevision
541
from shutil import rmtree
543
br_from = find_branch(from_location)
545
if e.errno == errno.ENOENT:
546
raise BzrCommandError('Source location "%s" does not exist.' %
551
if to_location is None:
552
to_location = os.path.basename(from_location.rstrip("/\\"))
555
os.mkdir(to_location)
557
if e.errno == errno.EEXIST:
558
raise BzrCommandError('Target directory "%s" already exists.' %
560
if e.errno == errno.ENOENT:
561
raise BzrCommandError('Parent of "%s" does not exist.' %
565
br_to = Branch(to_location, init=True)
568
br_to.update_revisions(br_from, stop_revision=revision)
569
except NoSuchRevision:
571
msg = "The branch %s has no revision %d." % (from_location,
573
raise BzrCommandError(msg)
574
merge((to_location, -1), (to_location, 0), this_dir=to_location,
575
check_clean=False, ignore_zero=True)
576
from_location = pull_loc(br_from)
577
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
580
def pull_loc(branch):
581
# TODO: Should perhaps just make attribute be 'base' in
582
# RemoteBranch and Branch?
583
if hasattr(branch, "baseurl"):
584
return branch.baseurl
590
286
class cmd_renames(Command):
591
287
"""Show list of renamed files.
827
456
takes_args = ['filename?']
828
457
def run(self, filename=None):
829
458
"""Print the branch root."""
830
from branch import find_branch
831
b = find_branch(filename)
832
print getattr(b, 'base', None) or getattr(b, 'baseurl')
459
print bzrlib.branch.find_branch_root(filename)
835
463
class cmd_log(Command):
836
464
"""Show log of this branch.
838
To request a range of logs, you can use the command -r begin:end
839
-r revision requests a specific revision, -r :end or -r begin: are
842
TODO: Make --revision support uuid: and hash: [future tag:] notation.
466
TODO: Options to show ids; to limit range; etc.
846
takes_args = ['filename?']
847
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
849
def run(self, filename=None, timezone='original',
854
from bzrlib import show_log, find_branch
857
direction = (forward and 'forward') or 'reverse'
860
b = find_branch(filename)
861
fp = b.relpath(filename)
863
file_id = b.read_working_inventory().path2id(fp)
865
file_id = None # points to branch root
871
revision = [None, None]
872
elif isinstance(revision, int):
873
revision = [revision, revision]
878
assert len(revision) == 2
880
mutter('encoding log as %r' % bzrlib.user_encoding)
882
# use 'replace' so that we don't abort if trying to write out
883
# in e.g. the default C locale.
884
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
887
show_timezone=timezone,
892
start_revision=revision[0],
893
end_revision=revision[1])
897
class cmd_touching_revisions(Command):
898
"""Return revision-ids which affected a particular file.
900
A more user-friendly interface is "bzr log FILE"."""
902
takes_args = ["filename"]
903
def run(self, filename):
905
inv = b.read_working_inventory()
906
file_id = inv.path2id(b.relpath(filename))
907
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
908
print "%6d %s" % (revno, what)
468
takes_options = ['timezone', 'verbose']
469
def run(self, timezone='original', verbose=False):
470
Branch('.').write_log(show_timezone=timezone, verbose=verbose)
911
473
class cmd_ls(Command):
947
509
class cmd_ignore(Command):
948
"""Ignore a command or pattern.
950
To remove patterns from the ignore list, edit the .bzrignore file.
952
If the pattern contains a slash, it is compared to the whole path
953
from the branch root. Otherwise, it is comapred to only the last
954
component of the path.
956
Ignore patterns are case-insensitive on case-insensitive systems.
958
Note: wildcards must be quoted from the shell on Unix.
961
bzr ignore ./Makefile
510
"""Ignore a command or pattern"""
964
511
takes_args = ['name_pattern']
966
513
def run(self, name_pattern):
967
from bzrlib.atomicfile import AtomicFile
971
ifn = b.abspath('.bzrignore')
973
if os.path.exists(ifn):
976
igns = f.read().decode('utf-8')
982
# TODO: If the file already uses crlf-style termination, maybe
983
# we should use that for the newly added lines?
985
if igns and igns[-1] != '\n':
987
igns += name_pattern + '\n'
990
f = AtomicFile(ifn, 'wt')
991
f.write(igns.encode('utf-8'))
516
# XXX: This will fail if it's a hardlink; should use an AtomicFile class.
517
f = open(b.abspath('.bzrignore'), 'at')
518
f.write(name_pattern + '\n')
996
521
inv = b.working_tree().inventory
997
522
if inv.path2id('.bzrignore'):
1023
546
bzr lookup-revision 33
1026
takes_args = ['revno']
1028
549
def run(self, revno):
1030
551
revno = int(revno)
1031
552
except ValueError:
1032
raise BzrCommandError("not a valid revision-number: %r" % revno)
1034
print Branch('.').lookup_revision(revno)
553
raise BzrError("not a valid revision-number: %r" % revno)
555
print Branch('.').lookup_revision(revno) or NONE_STRING
1037
559
class cmd_export(Command):
1038
560
"""Export past revision to destination directory.
1040
If no revision is specified this exports the last committed revision.
1042
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1043
given, exports to a directory (equivalent to --format=dir)."""
1044
# TODO: list known exporters
562
If no revision is specified this exports the last committed revision."""
1045
563
takes_args = ['dest']
1046
takes_options = ['revision', 'format']
1047
def run(self, dest, revision=None, format='dir'):
564
takes_options = ['revision']
565
def run(self, dest, revno=None):
1049
if revision == None:
1050
rh = b.revision_history()[-1]
568
rh = b.revision_history[-1]
1052
rh = b.lookup_revision(int(revision))
570
rh = b.lookup_revision(int(revno))
1053
571
t = b.revision_tree(rh)
1054
t.export(dest, format)
1057
575
class cmd_cat(Command):
1078
596
class cmd_commit(Command):
1079
597
"""Commit changes into a new revision.
1081
If selected files are specified, only changes to those files are
1082
committed. If a directory is specified then its contents are also
1085
A selected-file commit may fail in some cases where the committed
1086
tree would be invalid, such as trying to commit a file in a
1087
newly-added directory that is not itself committed.
599
TODO: Commit only selected files.
1089
601
TODO: Run hooks on tree to-be-committed, and after commit.
1091
603
TODO: Strict commit that fails if there are unknown or deleted files.
1093
takes_args = ['selected*']
1094
takes_options = ['message', 'file', 'verbose']
1095
aliases = ['ci', 'checkin']
1097
def run(self, message=None, file=None, verbose=True, selected_list=None):
1098
from bzrlib.commit import commit
1100
## Warning: shadows builtin file()
1101
if not message and not file:
1102
raise BzrCommandError("please specify a commit message",
1103
["use either --message or --file"])
1104
elif message and file:
1105
raise BzrCommandError("please specify either --message or --file")
1109
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1112
commit(b, message, verbose=verbose, specific_files=selected_list)
605
takes_options = ['message', 'verbose']
607
def run(self, message=None, verbose=False):
609
raise BzrCommandError("please specify a commit message")
610
Branch('.').commit(message, verbose=verbose)
1115
613
class cmd_check(Command):
1158
638
"""Run internal test suite"""
1161
from bzrlib.selftest import selftest
1162
return int(not selftest())
641
failures, tests = 0, 0
643
import doctest, bzrlib.store, bzrlib.tests
644
bzrlib.trace.verbose = False
646
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
647
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
648
mf, mt = doctest.testmod(m)
651
print '%-40s %3d tests' % (m.__name__, mt),
653
print '%3d FAILED!' % mf
657
print '%-40s %3d tests' % ('total', tests),
659
print '%3d FAILED!' % failures
1165
665
class cmd_version(Command):
1166
"""Show version of bzr."""
666
"""Show version of bzr"""
1170
670
def show_version():
1171
671
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1172
# is bzrlib itself in a branch?
1173
bzrrev = bzrlib.get_bzr_revision()
1175
print " (bzr checkout, revision %d {%s})" % bzrrev
1176
672
print bzrlib.__copyright__
1177
673
print "http://bazaar-ng.org/"
1188
684
print "it sure does!"
1190
def parse_spec(spec):
1192
>>> parse_spec(None)
1194
>>> parse_spec("./")
1196
>>> parse_spec("../@")
1198
>>> parse_spec("../f/@35")
1204
parsed = spec.split('/@')
1205
assert len(parsed) == 2
1209
parsed[1] = int(parsed[1])
1210
assert parsed[1] >=0
1212
parsed = [spec, None]
1217
class cmd_merge(Command):
1218
"""Perform a three-way merge of trees.
1220
The SPEC parameters are working tree or revision specifiers. Working trees
1221
are specified using standard paths or urls. No component of a directory
1222
path may begin with '@'.
1224
Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1226
Revisions are specified using a dirname/@revno pair, where dirname is the
1227
branch directory and revno is the revision within that branch. If no revno
1228
is specified, the latest revision is used.
1230
Revision examples: './@127', 'foo/@', '../@1'
1232
The OTHER_SPEC parameter is required. If the BASE_SPEC parameter is
1233
not supplied, the common ancestor of OTHER_SPEC the current branch is used
1236
merge refuses to run if there are any uncommitted changes, unless
1239
takes_args = ['other_spec', 'base_spec?']
1240
takes_options = ['force']
1242
def run(self, other_spec, base_spec=None, force=False):
1243
from bzrlib.merge import merge
1244
merge(parse_spec(other_spec), parse_spec(base_spec),
1245
check_clean=(not force))
1248
class cmd_revert(Command):
1249
"""Reverse all changes since the last commit.
1251
Only versioned files are affected.
1253
TODO: Store backups of any files that will be reverted, so
1254
that the revert can be undone.
1256
takes_options = ['revision']
1258
def run(self, revision=-1):
1259
from bzrlib.merge import merge
1260
merge(('.', revision), parse_spec('.'),
1265
687
class cmd_assert_fail(Command):
1266
688
"""Test reporting of assertion failures"""
1275
697
For a list of all available commands, say 'bzr help commands'."""
1276
698
takes_args = ['topic?']
1279
700
def run(self, topic=None):
1284
class cmd_update_stat_cache(Command):
1285
"""Update stat-cache mapping inodes to SHA-1 hashes.
1287
For testing only."""
1292
statcache.update_cache(b.base, b.read_working_inventory())
704
def help(topic=None):
707
elif topic == 'commands':
710
help_on_command(topic)
713
def help_on_command(cmdname):
714
cmdname = str(cmdname)
716
from inspect import getdoc
717
topic, cmdclass = get_cmd_class(cmdname)
719
doc = getdoc(cmdclass)
721
raise NotImplementedError("sorry, no detailed help yet for %r" % cmdname)
724
short, rest = doc.split('\n', 1)
729
print 'usage: bzr ' + topic,
730
for aname in cmdclass.takes_args:
731
aname = aname.upper()
732
if aname[-1] in ['$', '+']:
733
aname = aname[:-1] + '...'
734
elif aname[-1] == '?':
735
aname = '[' + aname[:-1] + ']'
736
elif aname[-1] == '*':
737
aname = '[' + aname[:-1] + '...]'
744
if cmdclass.takes_options:
747
for on in cmdclass.takes_options:
752
"""List all commands"""
756
for k, v in globals().items():
757
if k.startswith('cmd_'):
758
accu.append((k[4:].replace('_','-'), v))
760
for cmdname, cmdclass in accu:
764
help = inspect.getdoc(cmdclass)
766
print " " + help.split('\n', 1)[0]
769
######################################################################
1296
773
# list of all available options; the rhs can be either None for an
1372
830
if optname not in OPTIONS:
1373
raise BzrError('unknown long option %r' % a)
831
bailout('unknown long option %r' % a)
1375
833
shortopt = a[1:]
1376
if shortopt in SHORT_OPTIONS:
1377
# Multi-character options must have a space to delimit
1379
optname = SHORT_OPTIONS[shortopt]
1381
# Single character short options, can be chained,
1382
# and have their value appended to their name
1384
if shortopt not in SHORT_OPTIONS:
1385
# We didn't find the multi-character name, and we
1386
# didn't find the single char name
1387
raise BzrError('unknown short option %r' % a)
1388
optname = SHORT_OPTIONS[shortopt]
1391
# There are extra things on this option
1392
# see if it is the value, or if it is another
1394
optargfn = OPTIONS[optname]
1395
if optargfn is None:
1396
# This option does not take an argument, so the
1397
# next entry is another short option, pack it back
1399
argv.insert(0, '-' + a[2:])
1401
# This option takes an argument, so pack it
834
if shortopt not in SHORT_OPTIONS:
835
bailout('unknown short option %r' % a)
836
optname = SHORT_OPTIONS[shortopt]
1405
838
if optname in opts:
1406
839
# XXX: Do we ever want to support this, e.g. for -r?
1407
raise BzrError('repeated option %r' % a)
840
bailout('repeated option %r' % a)
1409
842
optargfn = OPTIONS[optname]
1411
844
if optarg == None:
1413
raise BzrError('option %r needs an argument' % a)
846
bailout('option %r needs an argument' % a)
1415
848
optarg = argv.pop(0)
1416
849
opts[optname] = optargfn(optarg)
1418
851
if optarg != None:
1419
raise BzrError('option %r takes no argument' % optname)
852
bailout('option %r takes no argument' % optname)
1420
853
opts[optname] = True