22
22
from bzrlib.trace import mutter, note, log_error
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
24
from bzrlib.branch import find_branch
25
from bzrlib import BZRDIR
31
def register_command(cmd):
32
"Utility function to help register a command"
35
if k.startswith("cmd_"):
36
k_unsquished = _unsquish_command_name(k)
39
if not plugin_cmds.has_key(k_unsquished):
40
plugin_cmds[k_unsquished] = cmd
42
log_error('Two plugins defined the same command: %r' % k)
43
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
29
46
def _squish_command_name(cmd):
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):
90
def _get_cmd_dict(plugins_override=True):
145
92
for k, v in globals().iteritems():
146
93
if k.startswith("cmd_"):
147
94
d[_unsquish_command_name(k)] = v
149
d.update(_find_plugins())
95
# If we didn't load plugins, the plugin_cmds dict will be empty
99
d2 = plugin_cmds.copy()
152
def get_all_cmds(include_plugins=True):
105
def get_all_cmds(plugins_override=True):
153
106
"""Return canonical name and class for all registered commands."""
154
for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
107
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
158
def get_cmd_class(cmd,include_plugins=True):
111
def get_cmd_class(cmd, plugins_override=True):
159
112
"""Return the canonical name and command class for a command.
161
114
cmd = str(cmd) # not unicode
163
116
# first look up this command under the specified name
164
cmds = _get_cmd_dict(include_plugins=include_plugins)
117
cmds = _get_cmd_dict(plugins_override=plugins_override)
166
119
return cmd, cmds[cmd]
351
304
def run(self, all=False, show_ids=False, file_list=None):
353
b = Branch(file_list[0])
306
b = find_branch(file_list[0])
354
307
file_list = [b.relpath(x) for x in file_list]
355
308
# special case: only one path was given and it's the root
357
310
if file_list == ['']:
362
315
status.show_status(b, show_unchanged=all, show_ids=show_ids,
363
316
specific_files=file_list)
406
360
takes_options = ['verbose', 'no-recurse']
408
362
def run(self, file_list, verbose=False, no_recurse=False):
409
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
363
from bzrlib.add import smart_add
364
smart_add(file_list, verbose, not no_recurse)
368
class cmd_mkdir(Command):
369
"""Create a new versioned directory.
371
This is equivalent to creating the directory and then adding it.
373
takes_args = ['dir+']
375
def run(self, dir_list):
382
b.add([d], verbose=True)
412
385
class cmd_relpath(Command):
495
468
def run(self, location=None):
496
469
from bzrlib.merge import merge
471
from shutil import rmtree
474
br_to = find_branch('.')
500
475
stored_loc = None
502
477
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
503
478
except IOError, e:
504
if errno == errno.ENOENT:
479
if e.errno != errno.ENOENT:
506
481
if location is None:
507
location = stored_loc
509
raise BzrCommandError("No pull location known or specified.")
510
from branch import find_branch, DivergedBranches
482
if stored_loc is None:
483
raise BzrCommandError("No pull location known or specified.")
485
print "Using last location: %s" % stored_loc
486
location = stored_loc
487
cache_root = tempfile.mkdtemp()
488
from bzrlib.branch import DivergedBranches
511
489
br_from = find_branch(location)
512
490
location = pull_loc(br_from)
513
491
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")
493
from branch import find_cached_branch, DivergedBranches
494
br_from = find_cached_branch(location, cache_root)
495
location = pull_loc(br_from)
496
old_revno = br_to.revno()
498
br_to.update_revisions(br_from)
499
except DivergedBranches:
500
raise BzrCommandError("These branches have diverged."
503
merge(('.', -1), ('.', old_revno), check_clean=False)
504
if location != stored_loc:
505
br_to.controlfile("x-pull", "wb").write(location + "\n")
537
523
def run(self, from_location, to_location=None, revision=None):
539
525
from bzrlib.merge import merge
540
from branch import find_branch, DivergedBranches, NoSuchRevision
526
from bzrlib.branch import DivergedBranches, NoSuchRevision, \
527
find_cached_branch, Branch
541
528
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")
529
from meta_store import CachedStore
531
cache_root = tempfile.mkdtemp()
534
br_from = find_cached_branch(from_location, cache_root)
536
if e.errno == errno.ENOENT:
537
raise BzrCommandError('Source location "%s" does not'
538
' exist.' % to_location)
542
if to_location is None:
543
to_location = os.path.basename(from_location.rstrip("/\\"))
546
os.mkdir(to_location)
548
if e.errno == errno.EEXIST:
549
raise BzrCommandError('Target directory "%s" already'
550
' exists.' % to_location)
551
if e.errno == errno.ENOENT:
552
raise BzrCommandError('Parent of "%s" does not exist.' %
556
br_to = Branch(to_location, init=True)
559
br_to.update_revisions(br_from, stop_revision=revision)
560
except NoSuchRevision:
562
msg = "The branch %s has no revision %d." % (from_location,
564
raise BzrCommandError(msg)
565
merge((to_location, -1), (to_location, 0), this_dir=to_location,
566
check_clean=False, ignore_zero=True)
567
from_location = pull_loc(br_from)
568
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
580
573
def pull_loc(branch):
672
664
"""Display list of revision ids on this branch."""
675
for patchid in Branch('.').revision_history():
667
for patchid in find_branch('.').revision_history():
679
671
class cmd_directories(Command):
680
672
"""Display list of versioned directories in this branch."""
682
for name, ie in Branch('.').read_working_inventory().directories():
674
for name, ie in find_branch('.').read_working_inventory().directories():
846
837
takes_args = ['filename?']
847
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
838
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long']
849
840
def run(self, filename=None, timezone='original',
854
from bzrlib import show_log, find_branch
846
from bzrlib.branch import find_branch
847
from bzrlib.log import log_formatter, show_log
857
850
direction = (forward and 'forward') or 'reverse'
1097
1099
def run(self, message=None, file=None, verbose=True, selected_list=None):
1098
1100
from bzrlib.commit import commit
1101
from bzrlib.osutils import get_text_message
1100
1103
## Warning: shadows builtin file()
1101
1104
if not message and not file:
1102
raise BzrCommandError("please specify a commit message",
1103
["use either --message or --file"])
1107
catcher = cStringIO.StringIO()
1108
sys.stdout = catcher
1109
cmd_status({"file_list":selected_list}, {})
1110
info = catcher.getvalue()
1112
message = get_text_message(info)
1115
raise BzrCommandError("please specify a commit message",
1116
["use either --message or --file"])
1104
1117
elif message and file:
1105
1118
raise BzrCommandError("please specify either --message or --file")
1245
1258
check_clean=(not force))
1248
1262
class cmd_revert(Command):
1263
"""Restore selected files from a previous revision.
1265
takes_args = ['file+']
1266
def run(self, file_list):
1267
from bzrlib.branch import find_branch
1272
b = find_branch(file_list[0])
1274
b.revert([b.relpath(f) for f in file_list])
1277
class cmd_merge_revert(Command):
1249
1278
"""Reverse all changes since the last commit.
1251
1280
Only versioned files are affected.
1514
def _parse_master_args(argv):
1515
"""Parse the arguments that always go with the original command.
1516
These are things like bzr --no-plugins, etc.
1518
There are now 2 types of option flags. Ones that come *before* the command,
1519
and ones that come *after* the command.
1520
Ones coming *before* the command are applied against all possible commands.
1521
And are generally applied before plugins are loaded.
1523
The current list are:
1524
--builtin Allow plugins to load, but don't let them override builtin commands,
1525
they will still be allowed if they do not override a builtin.
1526
--no-plugins Don't load any plugins. This lets you get back to official source
1528
--profile Enable the hotspot profile before running the command.
1529
For backwards compatibility, this is also a non-master option.
1530
--version Spit out the version of bzr that is running and exit.
1531
This is also a non-master option.
1532
--help Run help and exit, also a non-master option (I think that should stay, though)
1534
>>> argv, opts = _parse_master_args(['bzr', '--test'])
1535
Traceback (most recent call last):
1537
BzrCommandError: Invalid master option: 'test'
1538
>>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
1541
>>> print opts['version']
1543
>>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
1545
['command', '--more-options']
1546
>>> print opts['profile']
1548
>>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
1551
>>> print opts['no-plugins']
1553
>>> print opts['profile']
1555
>>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
1557
['command', '--profile']
1558
>>> print opts['profile']
1561
master_opts = {'builtin':False,
1568
# This is the point where we could hook into argv[0] to determine
1569
# what front-end is supposed to be run
1570
# For now, we are just ignoring it.
1571
cmd_name = argv.pop(0)
1573
if arg[:2] != '--': # at the first non-option, we return the rest
1575
arg = arg[2:] # Remove '--'
1576
if arg not in master_opts:
1577
# We could say that this is not an error, that we should
1578
# just let it be handled by the main section instead
1579
raise BzrCommandError('Invalid master option: %r' % arg)
1580
argv.pop(0) # We are consuming this entry
1581
master_opts[arg] = True
1582
return argv, master_opts
1474
1586
def run_bzr(argv):
1475
1587
"""Execute a command.
1480
1592
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1482
include_plugins=True
1484
args, opts = parse_args(argv[1:])
1595
# some options like --builtin and --no-plugins have special effects
1596
argv, master_opts = _parse_master_args(argv)
1597
if not master_opts['no-plugins']:
1598
from bzrlib.plugin import load_plugins
1601
args, opts = parse_args(argv)
1603
if master_opts['help']:
1604
from bzrlib.help import help
1485
1611
if 'help' in opts:
1612
from bzrlib.help import help
1492
1618
elif 'version' in opts:
1505
canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
1631
plugins_override = not (master_opts['builtin'])
1632
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1634
profile = master_opts['profile']
1635
# For backwards compatibility, I would rather stick with --profile being a
1636
# master/global option
1508
1637
if 'profile' in opts:
1510
1639
del opts['profile']
1514
1641
# check options are reasonable
1515
1642
allowed = cmd_class.takes_options