22
from bzrlib.trace import mutter, note, log_error, warning
22
from bzrlib.trace import mutter, note, log_error
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
from bzrlib.branch import find_branch
25
from bzrlib import BZRDIR
24
from bzrlib.osutils import quotefn
25
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
31
def register_command(cmd):
32
def register_plugin_command(cmd):
32
33
"Utility function to help register a command"
51
52
assert cmd.startswith("cmd_")
52
53
return cmd[4:].replace('_','-')
55
55
def _parse_revision_str(revstr):
56
"""This handles a revision string -> revno.
58
This always returns a list. The list will have one element for
60
It supports integers directly, but everything else it
61
defers for passing to Branch.get_revision_info()
63
>>> _parse_revision_str('234')
65
>>> _parse_revision_str('234..567')
67
>>> _parse_revision_str('..')
69
>>> _parse_revision_str('..234')
71
>>> _parse_revision_str('234..')
73
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
75
>>> _parse_revision_str('234....789') # Error?
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')
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')
93
>>> _parse_revision_str('-5')
95
>>> _parse_revision_str('123a')
97
>>> _parse_revision_str('abc')
56
"""This handles a revision string -> revno.
58
There are several possibilities:
61
'234:345' -> [234, 345]
65
In the future we will also support:
66
'uuid:blah-blah-blah' -> ?
67
'hash:blahblahblah' -> ?
101
old_format_re = re.compile('\d*:\d*')
102
m = old_format_re.match(revstr)
104
warning('Colon separator for revision numbers is deprecated.'
107
for rev in revstr.split(':'):
109
revs.append(int(rev))
114
for x in revstr.split('..'):
71
if revstr.find(':') != -1:
72
revs = revstr.split(':')
74
raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
79
revs[0] = int(revs[0])
84
revs[1] = int(revs[1])
125
def get_merge_type(typestring):
126
"""Attempt to find the merge class/factory associated with a string."""
127
from merge import merge_types
129
return merge_types[typestring][0]
131
templ = '%s%%7s: %%s' % (' '*12)
132
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
133
type_list = '\n'.join(lines)
134
msg = "No known merge type %s. Supported types are:\n%s" %\
135
(typestring, type_list)
136
raise BzrCommandError(msg)
140
91
def _get_cmd_dict(plugins_override=True):
214
165
assert isinstance(arguments, dict)
215
166
cmdargs = options.copy()
216
167
cmdargs.update(arguments)
217
if self.__doc__ == Command.__doc__:
218
from warnings import warn
219
warn("No help message set for %r" % self)
168
assert self.__doc__ != Command.__doc__, \
169
("No help message set for %r" % self)
220
170
self.status = self.run(**cmdargs)
221
if self.status is None:
349
297
directory is shown. Otherwise, only the status of the specified
350
298
files or directories is reported. If a directory is given, status
351
299
is reported for everything inside that directory.
353
If a revision is specified, the changes since that revision are shown.
355
301
takes_args = ['file*']
356
takes_options = ['all', 'show-ids', 'revision']
302
takes_options = ['all', 'show-ids']
357
303
aliases = ['st', 'stat']
359
305
def run(self, all=False, show_ids=False, file_list=None):
361
b = find_branch(file_list[0])
307
b = Branch(file_list[0])
362
308
file_list = [b.relpath(x) for x in file_list]
363
309
# special case: only one path was given and it's the root
365
311
if file_list == ['']:
370
from bzrlib.status import show_status
371
show_status(b, show_unchanged=all, show_ids=show_ids,
372
specific_files=file_list)
316
status.show_status(b, show_unchanged=all, show_ids=show_ids,
317
specific_files=file_list)
375
320
class cmd_cat_revision(Command):
389
333
This is equal to the number of revisions on this branch."""
391
print find_branch('.').revno()
393
class cmd_revision_info(Command):
394
"""Show revision number and revision id for a given revision identifier.
397
takes_args = ['revision_info*']
398
takes_options = ['revision']
399
def run(self, revision=None, revision_info_list=None):
400
from bzrlib.branch import find_branch
403
if revision is not None:
404
revs.extend(revision)
405
if revision_info_list is not None:
406
revs.extend(revision_info_list)
408
raise BzrCommandError('You must supply a revision identifier')
413
print '%4d %s' % b.get_revision_info(rev)
335
print Branch('.').revno()
416
338
class cmd_add(Command):
426
348
whether already versioned or not, are searched for files or
427
349
subdirectories that are neither versioned or ignored, and these
428
350
are added. This search proceeds recursively into versioned
429
directories. If no names are given '.' is assumed.
431
Therefore simply saying 'bzr add' will version all files that
353
Therefore simply saying 'bzr add .' will version all files that
432
354
are currently unknown.
434
356
TODO: Perhaps adding a file whose directly is not versioned should
435
357
recursively add that parent, rather than giving an error?
437
takes_args = ['file*']
359
takes_args = ['file+']
438
360
takes_options = ['verbose', 'no-recurse']
440
362
def run(self, file_list, verbose=False, no_recurse=False):
441
from bzrlib.add import smart_add
442
smart_add(file_list, verbose, not no_recurse)
446
class cmd_mkdir(Command):
447
"""Create a new versioned directory.
449
This is equivalent to creating the directory and then adding it.
451
takes_args = ['dir+']
453
def run(self, dir_list):
460
b.add([d], verbose=True)
363
bzrlib.add.smart_add(file_list, verbose, not no_recurse)
463
366
class cmd_relpath(Command):
475
378
takes_options = ['revision', 'show-ids']
477
380
def run(self, revision=None, show_ids=False):
479
382
if revision == None:
480
383
inv = b.read_working_inventory()
482
if len(revision) > 1:
483
raise BzrCommandError('bzr inventory --revision takes'
484
' exactly one revision identifier')
485
inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
385
inv = b.get_revision_inventory(b.lookup_revision(revision))
487
387
for path, entry in inv.entries():
566
464
print "Using last location: %s" % stored_loc
567
465
location = stored_loc
568
cache_root = tempfile.mkdtemp()
569
from bzrlib.branch import DivergedBranches
466
from branch import find_branch, DivergedBranches
570
467
br_from = find_branch(location)
571
468
location = pull_loc(br_from)
572
469
old_revno = br_to.revno()
574
from branch import find_cached_branch, DivergedBranches
575
br_from = find_cached_branch(location, cache_root)
576
location = pull_loc(br_from)
577
old_revno = br_to.revno()
579
br_to.update_revisions(br_from)
580
except DivergedBranches:
581
raise BzrCommandError("These branches have diverged."
584
merge(('.', -1), ('.', old_revno), check_clean=False)
585
if location != stored_loc:
586
br_to.controlfile("x-pull", "wb").write(location + "\n")
471
br_to.update_revisions(br_from)
472
except DivergedBranches:
473
raise BzrCommandError("These branches have diverged. Try merge.")
475
merge(('.', -1), ('.', old_revno), check_clean=False)
476
if location != stored_loc:
477
br_to.controlfile("x-pull", "wb").write(location + "\n")
604
493
def run(self, from_location, to_location=None, revision=None):
606
495
from bzrlib.merge import merge
607
from bzrlib.branch import DivergedBranches, NoSuchRevision, \
608
find_cached_branch, Branch
496
from branch import find_branch, DivergedBranches, NoSuchRevision
609
497
from shutil import rmtree
610
from meta_store import CachedStore
612
cache_root = tempfile.mkdtemp()
616
elif len(revision) > 1:
617
raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
621
br_from = find_cached_branch(from_location, cache_root)
623
if e.errno == errno.ENOENT:
624
raise BzrCommandError('Source location "%s" does not'
625
' exist.' % to_location)
629
if to_location is None:
630
to_location = os.path.basename(from_location.rstrip("/\\"))
633
os.mkdir(to_location)
635
if e.errno == errno.EEXIST:
636
raise BzrCommandError('Target directory "%s" already'
637
' exists.' % to_location)
638
if e.errno == errno.ENOENT:
639
raise BzrCommandError('Parent of "%s" does not exist.' %
643
br_to = Branch(to_location, init=True)
645
br_to.set_root_id(br_from.get_root_id())
648
if revision[0] is None:
649
revno = br_from.revno()
651
revno, rev_id = br_from.get_revision_info(revision[0])
653
br_to.update_revisions(br_from, stop_revision=revno)
654
except NoSuchRevision:
656
msg = "The branch %s has no revision %d." % (from_location,
658
raise BzrCommandError(msg)
660
merge((to_location, -1), (to_location, 0), this_dir=to_location,
661
check_clean=False, ignore_zero=True)
662
from_location = pull_loc(br_from)
663
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
499
br_from = find_branch(from_location)
501
if e.errno == errno.ENOENT:
502
raise BzrCommandError('Source location "%s" does not exist.' %
507
if to_location is None:
508
to_location = os.path.basename(from_location.rstrip("/\\"))
511
os.mkdir(to_location)
513
if e.errno == errno.EEXIST:
514
raise BzrCommandError('Target directory "%s" already exists.' %
516
if e.errno == errno.ENOENT:
517
raise BzrCommandError('Parent of "%s" does not exist.' %
521
br_to = Branch(to_location, init=True)
524
br_to.update_revisions(br_from, stop_revision=revision)
525
except NoSuchRevision:
527
msg = "The branch %s has no revision %d." % (from_location,
529
raise BzrCommandError(msg)
530
merge((to_location, -1), (to_location, 0), this_dir=to_location,
531
check_clean=False, ignore_zero=True)
532
from_location = pull_loc(br_from)
533
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
668
536
def pull_loc(branch):
759
628
"""Display list of revision ids on this branch."""
762
for patchid in find_branch('.').revision_history():
631
for patchid in Branch('.').revision_history():
766
635
class cmd_directories(Command):
767
636
"""Display list of versioned directories in this branch."""
769
for name, ie in find_branch('.').read_working_inventory().directories():
638
for name, ie in Branch('.').read_working_inventory().directories():
829
698
# just pointing to top-of-tree
834
# TODO: Make show_diff support taking 2 arguments
836
if revision is not None:
837
if len(revision) != 1:
838
raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
839
base_rev = revision[0]
841
show_diff(b, base_rev, specific_files=file_list,
703
show_diff(b, revision, specific_files=file_list,
842
704
external_diff_options=diff_options)
872
734
"""List files modified in working tree."""
875
from bzrlib.diff import compare_trees
878
td = compare_trees(b.basis_tree(), b.working_tree())
880
for path, id, kind in td.modified:
739
inv = b.read_working_inventory()
740
sc = statcache.update_cache(b, inv)
741
basis = b.basis_tree()
742
basis_inv = basis.inventory
744
# We used to do this through iter_entries(), but that's slow
745
# when most of the files are unmodified, as is usually the
746
# case. So instead we iterate by inventory entry, and only
747
# calculate paths as necessary.
749
for file_id in basis_inv:
750
cacheentry = sc.get(file_id)
751
if not cacheentry: # deleted
753
ie = basis_inv[file_id]
754
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
755
path = inv.id2path(file_id)
919
795
-r revision requests a specific revision, -r :end or -r begin: are
922
--message allows you to give a regular expression, which will be evaluated
923
so that only matching entries will be displayed.
925
798
TODO: Make --revision support uuid: and hash: [future tag:] notation.
929
802
takes_args = ['filename?']
930
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
803
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
932
805
def run(self, filename=None, timezone='original',
939
from bzrlib.branch import find_branch
940
from bzrlib.log import log_formatter, show_log
810
from bzrlib import show_log, find_branch
943
813
direction = (forward and 'forward') or 'reverse'
953
823
b = find_branch('.')
959
elif len(revision) == 1:
960
rev1 = rev2 = b.get_revision_info(revision[0])[0]
961
elif len(revision) == 2:
962
rev1 = b.get_revision_info(revision[0])[0]
963
rev2 = b.get_revision_info(revision[1])[0]
827
revision = [None, None]
828
elif isinstance(revision, int):
829
revision = [revision, revision]
965
raise BzrCommandError('bzr log --revision takes one or two values.')
834
assert len(revision) == 2
972
836
mutter('encoding log as %r' % bzrlib.user_encoding)
1142
996
If no revision is specified this exports the last committed revision.
1144
998
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1145
given, try to find the format with the extension. If no extension
1146
is found exports to a directory (equivalent to --format=dir).
1148
Root may be the top directory for tar, tgz and tbz2 formats. If none
1149
is given, the top directory will be the root name of the file."""
999
given, exports to a directory (equivalent to --format=dir)."""
1150
1000
# TODO: list known exporters
1151
1001
takes_args = ['dest']
1152
takes_options = ['revision', 'format', 'root']
1153
def run(self, dest, revision=None, format=None, root=None):
1155
b = find_branch('.')
1156
if revision is None:
1157
rev_id = b.last_patch()
1002
takes_options = ['revision', 'format']
1003
def run(self, dest, revision=None, format='dir'):
1005
if revision == None:
1006
rh = b.revision_history()[-1]
1159
if len(revision) != 1:
1160
raise BzrError('bzr export --revision takes exactly 1 argument')
1161
revno, rev_id = b.get_revision_info(revision[0])
1162
t = b.revision_tree(rev_id)
1163
root, ext = os.path.splitext(dest)
1165
if ext in (".tar",):
1167
elif ext in (".gz", ".tgz"):
1169
elif ext in (".bz2", ".tbz2"):
1173
t.export(dest, format, root)
1008
rh = b.lookup_revision(int(revision))
1009
t = b.revision_tree(rh)
1010
t.export(dest, format)
1176
1013
class cmd_cat(Command):
1212
1047
TODO: Strict commit that fails if there are unknown or deleted files.
1214
1049
takes_args = ['selected*']
1215
takes_options = ['message', 'file', 'verbose', 'unchanged']
1050
takes_options = ['message', 'file', 'verbose']
1216
1051
aliases = ['ci', 'checkin']
1218
def run(self, message=None, file=None, verbose=True, selected_list=None,
1220
from bzrlib.errors import PointlessCommit
1221
from bzrlib.osutils import get_text_message
1053
def run(self, message=None, file=None, verbose=True, selected_list=None):
1054
from bzrlib.commit import commit
1223
1056
## Warning: shadows builtin file()
1224
1057
if not message and not file:
1227
catcher = cStringIO.StringIO()
1228
sys.stdout = catcher
1229
cmd_status({"file_list":selected_list}, {})
1230
info = catcher.getvalue()
1232
message = get_text_message(info)
1235
raise BzrCommandError("please specify a commit message",
1236
["use either --message or --file"])
1058
raise BzrCommandError("please specify a commit message",
1059
["use either --message or --file"])
1237
1060
elif message and file:
1238
1061
raise BzrCommandError("please specify either --message or --file")
1242
1065
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1244
b = find_branch('.')
1247
b.commit(message, verbose=verbose,
1248
specific_files=selected_list,
1249
allow_pointless=unchanged)
1250
except PointlessCommit:
1251
# FIXME: This should really happen before the file is read in;
1252
# perhaps prepare the commit; get the message; then actually commit
1253
raise BzrCommandError("no changes to commit",
1254
["use --unchanged to commit anyhow"])
1068
commit(b, message, verbose=verbose, specific_files=selected_list)
1257
1071
class cmd_check(Command):
1266
1080
takes_args = ['dir?']
1268
1082
def run(self, dir='.'):
1269
from bzrlib.check import check
1270
check(find_branch(dir))
1274
class cmd_scan_cache(Command):
1277
from bzrlib.hashcache import HashCache
1284
print '%6d stats' % c.stat_count
1285
print '%6d in hashcache' % len(c._cache)
1286
print '%6d files removed from cache' % c.removed_count
1287
print '%6d hashes updated' % c.update_count
1288
print '%6d files changed too recently to cache' % c.danger_count
1084
bzrlib.check.check(Branch(dir))
1295
1088
class cmd_upgrade(Command):
1407
1193
--force is given.
1409
1195
takes_args = ['other_spec', 'base_spec?']
1410
takes_options = ['force', 'merge-type']
1196
takes_options = ['force']
1412
def run(self, other_spec, base_spec=None, force=False, merge_type=None):
1198
def run(self, other_spec, base_spec=None, force=False):
1413
1199
from bzrlib.merge import merge
1414
from bzrlib.merge_core import ApplyMerge3
1415
if merge_type is None:
1416
merge_type = ApplyMerge3
1417
1200
merge(parse_spec(other_spec), parse_spec(base_spec),
1418
check_clean=(not force), merge_type=merge_type)
1201
check_clean=(not force))
1421
1204
class cmd_revert(Command):
1422
1205
"""Reverse all changes since the last commit.
1424
Only versioned files are affected. Specify filenames to revert only
1425
those files. By default, any files that are changed will be backed up
1426
first. Backup files have a '~' appended to their name.
1207
Only versioned files are affected.
1209
TODO: Store backups of any files that will be reverted, so
1210
that the revert can be undone.
1428
takes_options = ['revision', 'no-backup']
1429
takes_args = ['file*']
1430
aliases = ['merge-revert']
1212
takes_options = ['revision']
1432
def run(self, revision=None, no_backup=False, file_list=None):
1214
def run(self, revision=-1):
1433
1215
from bzrlib.merge import merge
1434
if file_list is not None:
1435
if len(file_list) == 0:
1436
raise BzrCommandError("No files specified")
1437
if revision is None:
1439
elif len(revision) != 1:
1440
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1441
merge(('.', revision[0]), parse_spec('.'),
1216
merge(('.', revision), parse_spec('.'),
1442
1217
check_clean=False,
1444
backup_files=not no_backup,
1445
file_list=file_list)
1448
1221
class cmd_assert_fail(Command):
1464
1237
help.help(topic)
1469
class cmd_plugins(Command):
1240
class cmd_update_stat_cache(Command):
1241
"""Update stat-cache mapping inodes to SHA-1 hashes.
1243
For testing only."""
1473
import bzrlib.plugin
1474
from inspect import getdoc
1475
from pprint import pprint
1476
for plugin in bzrlib.plugin.all_plugins:
1477
print plugin.__path__[0]
1480
print '\t', d.split('\n')[0]
1482
#pprint(bzrlib.plugin.all_plugins)
1248
statcache.update_cache(b.base, b.read_working_inventory())
1538
1298
>>> parse_args('commit --message=biter'.split())
1539
1299
(['commit'], {'message': u'biter'})
1540
1300
>>> parse_args('log -r 500'.split())
1541
(['log'], {'revision': [500]})
1542
>>> parse_args('log -r500..600'.split())
1301
(['log'], {'revision': 500})
1302
>>> parse_args('log -r500:600'.split())
1543
1303
(['log'], {'revision': [500, 600]})
1544
>>> parse_args('log -vr500..600'.split())
1304
>>> parse_args('log -vr500:600'.split())
1545
1305
(['log'], {'verbose': True, 'revision': [500, 600]})
1546
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
1547
(['log'], {'revision': ['v500', 600]})
1306
>>> parse_args('log -rv500:600'.split()) #the r takes an argument
1307
Traceback (most recent call last):
1309
ValueError: invalid literal for int(): v500
1684
1446
This is also a non-master option.
1685
1447
--help Run help and exit, also a non-master option (I think that should stay, though)
1687
>>> argv, opts = _parse_master_args(['--test'])
1449
>>> argv, opts = _parse_master_args(['bzr', '--test'])
1688
1450
Traceback (most recent call last):
1690
1452
BzrCommandError: Invalid master option: 'test'
1691
>>> argv, opts = _parse_master_args(['--version', 'command'])
1453
>>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
1694
1456
>>> print opts['version']
1696
>>> argv, opts = _parse_master_args(['--profile', 'command', '--more-options'])
1458
>>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
1698
1460
['command', '--more-options']
1699
1461
>>> print opts['profile']
1701
>>> argv, opts = _parse_master_args(['--no-plugins', 'command'])
1463
>>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
1704
1466
>>> print opts['no-plugins']
1706
1468
>>> print opts['profile']
1708
>>> argv, opts = _parse_master_args(['command', '--profile'])
1470
>>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
1710
1472
['command', '--profile']
1711
1473
>>> print opts['profile']
1738
1504
This is similar to main(), but without all the trappings for
1739
1505
logging and error handling.
1742
The command-line arguments, without the program name.
1744
Returns a command status or raises an exception.
1746
1507
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1748
# some options like --builtin and --no-plugins have special effects
1749
argv, master_opts = _parse_master_args(argv)
1750
if not master_opts['no-plugins']:
1751
from bzrlib.plugin import load_plugins
1754
args, opts = parse_args(argv)
1756
if master_opts.get('help') or 'help' in opts:
1757
from bzrlib.help import help
1764
if 'version' in opts:
1768
if args and args[0] == 'builtin':
1769
include_plugins=False
1510
# some options like --builtin and --no-plugins have special effects
1511
argv, master_opts = _parse_master_args(argv)
1512
if 'no-plugins' not in master_opts:
1513
bzrlib.load_plugins()
1515
args, opts = parse_args(argv)
1517
if master_opts['help']:
1518
from bzrlib.help import help
1526
from bzrlib.help import help
1532
elif 'version' in opts:
1535
elif args and args[0] == 'builtin':
1536
include_plugins=False
1773
1538
cmd = str(args.pop(0))
1774
1539
except IndexError:
1775
print >>sys.stderr, "please try 'bzr help' for help"
1778
1545
plugins_override = not (master_opts['builtin'])
1779
1546
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)