19
import sys, os, time, os.path
23
from bzrlib.trace import mutter, note, log_error
24
from bzrlib.errors import bailout, BzrError, BzrCheckError, BzrCommandError
25
from bzrlib.osutils import quotefn, pumpfile, isdir, isfile
26
from bzrlib.tree import RevisionTree, EmptyTree, WorkingTree, Tree
27
from bzrlib.revision import Revision
28
from bzrlib import Branch, Inventory, InventoryEntry, ScratchBranch, BZRDIR, \
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
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__])
32
46
def _squish_command_name(cmd):
37
51
assert cmd.startswith("cmd_")
38
52
return cmd[4:].replace('_','-')
41
"""Return canonical name and class for all registered commands."""
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')
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('..'):
126
def _get_cmd_dict(plugins_override=True):
42
128
for k, v in globals().iteritems():
43
129
if k.startswith("cmd_"):
44
yield _unsquish_command_name(k), v
46
def get_cmd_class(cmd):
130
d[_unsquish_command_name(k)] = v
131
# If we didn't load plugins, the plugin_cmds dict will be empty
133
d.update(plugin_cmds)
135
d2 = plugin_cmds.copy()
141
def get_all_cmds(plugins_override=True):
142
"""Return canonical name and class for all registered commands."""
143
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
147
def get_cmd_class(cmd, plugins_override=True):
47
148
"""Return the canonical name and command class for a command.
49
150
cmd = str(cmd) # not unicode
51
152
# first look up this command under the specified name
153
cmds = _get_cmd_dict(plugins_override=plugins_override)
53
return cmd, globals()[_squish_command_name(cmd)]
155
return cmd, cmds[cmd]
57
159
# look for any command which claims this as an alias
58
for cmdname, cmdclass in get_all_cmds():
160
for cmdname, cmdclass in cmds.iteritems():
59
161
if cmd in cmdclass.aliases:
60
162
return cmdname, cmdclass
145
251
def __init__(self, path):
148
# TODO: If either of these fail, we should detect that and
149
# assume that path is not really a bzr plugin after all.
151
254
pipe = os.popen('%s --bzr-usage' % path, 'r')
152
255
self.takes_options = pipe.readline().split()
257
for opt in self.takes_options:
258
if not opt in OPTIONS:
259
raise BzrError("Unknown option '%s' returned by external command %s"
262
# TODO: Is there any way to check takes_args is valid here?
153
263
self.takes_args = pipe.readline().split()
265
if pipe.close() is not None:
266
raise BzrError("Failed funning '%s --bzr-usage'" % path)
156
268
pipe = os.popen('%s --bzr-help' % path, 'r')
157
269
self.__doc__ = pipe.read()
270
if pipe.close() is not None:
271
raise BzrError("Failed funning '%s --bzr-help'" % path)
160
273
def __call__(self, options, arguments):
161
274
Command.__init__(self, options, arguments)
189
303
class cmd_status(Command):
190
304
"""Display status summary.
192
For each file there is a single line giving its file state and name.
193
The name is that in the current revision unless it is deleted or
194
missing, in which case the old name is shown.
306
This reports on versioned and unknown files, reporting them
307
grouped by state. Possible states are:
310
Versioned in the working copy but not in the previous revision.
313
Versioned in the previous revision but removed or deleted
317
Path of this file changed from the previous revision;
318
the text may also have changed. This includes files whose
319
parent directory was renamed.
322
Text has changed since the previous revision.
325
Nothing about this file has changed since the previous revision.
326
Only shown with --all.
329
Not versioned and not matching an ignore pattern.
331
To see ignored files use 'bzr ignored'. For details in the
332
changes to file texts, use 'bzr diff'.
334
If no arguments are specified, the status of the entire working
335
directory is shown. Otherwise, only the status of the specified
336
files or directories is reported. If a directory is given, status
337
is reported for everything inside that directory.
339
If a revision is specified, the changes since that revision are shown.
196
341
takes_args = ['file*']
197
takes_options = ['all']
342
takes_options = ['all', 'show-ids', 'revision']
198
343
aliases = ['st', 'stat']
200
def run(self, all=False, file_list=None):
201
b = Branch('.', lock_mode='r')
202
b.show_status(show_all=all, file_list=file_list)
345
def run(self, all=False, show_ids=False, file_list=None):
347
b = find_branch(file_list[0])
348
file_list = [b.relpath(x) for x in file_list]
349
# special case: only one path was given and it's the root
351
if file_list == ['']:
356
from bzrlib.status import show_status
357
show_status(b, show_unchanged=all, show_ids=show_ids,
358
specific_files=file_list)
205
361
class cmd_cat_revision(Command):
233
412
whether already versioned or not, are searched for files or
234
413
subdirectories that are neither versioned or ignored, and these
235
414
are added. This search proceeds recursively into versioned
415
directories. If no names are given '.' is assumed.
238
Therefore simply saying 'bzr add .' will version all files that
417
Therefore simply saying 'bzr add' will version all files that
239
418
are currently unknown.
241
420
TODO: Perhaps adding a file whose directly is not versioned should
242
421
recursively add that parent, rather than giving an error?
244
takes_args = ['file+']
245
takes_options = ['verbose']
423
takes_args = ['file*']
424
takes_options = ['verbose', 'no-recurse']
247
def run(self, file_list, verbose=False):
248
bzrlib.add.smart_add(file_list, verbose)
426
def run(self, file_list, verbose=False, no_recurse=False):
427
from bzrlib.add import smart_add
428
smart_add(file_list, verbose, not no_recurse)
432
class cmd_mkdir(Command):
433
"""Create a new versioned directory.
435
This is equivalent to creating the directory and then adding it.
437
takes_args = ['dir+']
439
def run(self, dir_list):
446
b.add([d], verbose=True)
251
449
class cmd_relpath(Command):
252
450
"""Show path of a file relative to root"""
253
451
takes_args = ['filename']
255
454
def run(self, filename):
256
print Branch(filename).relpath(filename)
455
print find_branch(filename).relpath(filename)
260
459
class cmd_inventory(Command):
261
460
"""Show inventory of the current working copy or a revision."""
262
takes_options = ['revision']
461
takes_options = ['revision', 'show-ids']
264
def run(self, revision=None):
463
def run(self, revision=None, show_ids=False):
266
465
if revision == None:
267
466
inv = b.read_working_inventory()
269
inv = b.get_revision_inventory(b.lookup_revision(revision))
468
if len(revision) > 1:
469
raise BzrCommandError('bzr inventory --revision takes'
470
' exactly one revision identifier')
471
inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
271
for path, entry in inv.iter_entries():
272
print '%-50s %s' % (entry.file_id, path)
473
for path, entry in inv.entries():
475
print '%-50s %s' % (path, entry.file_id)
275
480
class cmd_move(Command):
304
509
takes_args = ['from_name', 'to_name']
306
511
def run(self, from_name, to_name):
308
513
b.rename_one(b.relpath(from_name), b.relpath(to_name))
519
class cmd_pull(Command):
520
"""Pull any changes from another branch into the current one.
522
If the location is omitted, the last-used location will be used.
523
Both the revision history and the working directory will be
526
This command only works on branches that have not diverged. Branches are
527
considered diverged if both branches have had commits without first
528
pulling from the other.
530
If branches have diverged, you can use 'bzr merge' to pull the text changes
531
from one into the other.
533
takes_args = ['location?']
535
def run(self, location=None):
536
from bzrlib.merge import merge
538
from shutil import rmtree
541
br_to = find_branch('.')
544
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
546
if e.errno != errno.ENOENT:
549
if stored_loc is None:
550
raise BzrCommandError("No pull location known or specified.")
552
print "Using last location: %s" % stored_loc
553
location = stored_loc
554
cache_root = tempfile.mkdtemp()
555
from bzrlib.branch import DivergedBranches
556
br_from = find_branch(location)
557
location = pull_loc(br_from)
558
old_revno = br_to.revno()
560
from branch import find_cached_branch, DivergedBranches
561
br_from = find_cached_branch(location, cache_root)
562
location = pull_loc(br_from)
563
old_revno = br_to.revno()
565
br_to.update_revisions(br_from)
566
except DivergedBranches:
567
raise BzrCommandError("These branches have diverged."
570
merge(('.', -1), ('.', old_revno), check_clean=False)
571
if location != stored_loc:
572
br_to.controlfile("x-pull", "wb").write(location + "\n")
578
class cmd_branch(Command):
579
"""Create a new copy of a branch.
581
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
582
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
584
To retrieve the branch as of a particular revision, supply the --revision
585
parameter, as in "branch foo/bar -r 5".
587
takes_args = ['from_location', 'to_location?']
588
takes_options = ['revision']
590
def run(self, from_location, to_location=None, revision=None):
592
from bzrlib.merge import merge
593
from bzrlib.branch import DivergedBranches, NoSuchRevision, \
594
find_cached_branch, Branch
595
from shutil import rmtree
596
from meta_store import CachedStore
598
cache_root = tempfile.mkdtemp()
602
elif len(revision) > 1:
603
raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
607
br_from = find_cached_branch(from_location, cache_root)
609
if e.errno == errno.ENOENT:
610
raise BzrCommandError('Source location "%s" does not'
611
' exist.' % to_location)
615
if to_location is None:
616
to_location = os.path.basename(from_location.rstrip("/\\"))
619
os.mkdir(to_location)
621
if e.errno == errno.EEXIST:
622
raise BzrCommandError('Target directory "%s" already'
623
' exists.' % to_location)
624
if e.errno == errno.ENOENT:
625
raise BzrCommandError('Parent of "%s" does not exist.' %
629
br_to = Branch(to_location, init=True)
631
br_to.set_root_id(br_from.get_root_id())
634
if revision[0] is None:
635
revno = br_from.revno()
637
revno, rev_id = br_from.get_revision_info(revision[0])
639
br_to.update_revisions(br_from, stop_revision=revno)
640
except NoSuchRevision:
642
msg = "The branch %s has no revision %d." % (from_location,
644
raise BzrCommandError(msg)
646
merge((to_location, -1), (to_location, 0), this_dir=to_location,
647
check_clean=False, ignore_zero=True)
648
from_location = pull_loc(br_from)
649
br_to.controlfile("x-pull", "wb").write(from_location + "\n")
654
def pull_loc(branch):
655
# TODO: Should perhaps just make attribute be 'base' in
656
# RemoteBranch and Branch?
657
if hasattr(branch, "baseurl"):
658
return branch.baseurl
312
664
class cmd_renames(Command):
313
665
"""Show list of renamed files.
377
733
takes_args = ['filename']
378
734
def run(self, filename):
735
b = find_branch(filename)
380
736
inv = b.inventory
381
737
fid = inv.path2id(b.relpath(filename))
383
bailout("%r is not a versioned file" % filename)
739
raise BzrError("%r is not a versioned file" % filename)
384
740
for fip in inv.get_idpath(fid):
388
744
class cmd_revision_history(Command):
389
745
"""Display list of revision ids on this branch."""
391
for patchid in Branch('.').revision_history():
748
for patchid in find_branch('.').revision_history():
395
752
class cmd_directories(Command):
396
753
"""Display list of versioned directories in this branch."""
398
for name, ie in Branch('.').read_working_inventory().directories():
755
for name, ie in find_branch('.').read_working_inventory().directories():
446
804
takes_args = ['file*']
447
takes_options = ['revision']
805
takes_options = ['revision', 'diff-options']
806
aliases = ['di', 'dif']
450
def run(self, revision=None, file_list=None):
808
def run(self, revision=None, file_list=None, diff_options=None):
451
809
from bzrlib.diff import show_diff
812
b = find_branch(file_list[0])
813
file_list = [b.relpath(f) for f in file_list]
814
if file_list == ['']:
815
# just pointing to top-of-tree
820
# TODO: Make show_diff support taking 2 arguments
822
if revision is not None:
823
if len(revision) != 1:
824
raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
825
base_rev = revision[0]
453
show_diff(Branch('.'), revision, file_list)
827
show_diff(b, base_rev, specific_files=file_list,
828
external_diff_options=diff_options)
483
858
"""List files modified in working tree."""
488
inv = b.read_working_inventory()
489
sc = statcache.update_cache(b, inv)
490
basis = b.basis_tree()
491
basis_inv = basis.inventory
493
# We used to do this through iter_entries(), but that's slow
494
# when most of the files are unmodified, as is usually the
495
# case. So instead we iterate by inventory entry, and only
496
# calculate paths as necessary.
498
for file_id in basis_inv:
499
cacheentry = sc.get(file_id)
500
if not cacheentry: # deleted
502
ie = basis_inv[file_id]
503
if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
504
path = inv.id2path(file_id)
861
from bzrlib.diff import compare_trees
864
td = compare_trees(b.basis_tree(), b.working_tree())
866
for path, id, kind in td.modified:
540
901
class cmd_log(Command):
541
902
"""Show log of this branch.
543
TODO: Option to limit range.
545
TODO: Perhaps show most-recent first with an option for last.
904
To request a range of logs, you can use the command -r begin:end
905
-r revision requests a specific revision, -r :end or -r begin: are
908
--message allows you to give a regular expression, which will be evaluated
909
so that only matching entries will be displayed.
911
TODO: Make --revision support uuid: and hash: [future tag:] notation.
547
915
takes_args = ['filename?']
548
takes_options = ['timezone', 'verbose', 'show-ids']
549
def run(self, filename=None, timezone='original', verbose=False, show_ids=False):
550
from branch import find_branch
551
b = find_branch((filename or '.'), lock_mode='r')
916
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
918
def run(self, filename=None, timezone='original',
925
from bzrlib.branch import find_branch
926
from bzrlib.log import log_formatter, show_log
929
direction = (forward and 'forward') or 'reverse'
553
filename = b.relpath(filename)
554
bzrlib.show_log(b, filename,
555
show_timezone=timezone,
932
b = find_branch(filename)
933
fp = b.relpath(filename)
935
file_id = b.read_working_inventory().path2id(fp)
937
file_id = None # points to branch root
945
elif len(revision) == 1:
946
rev1 = rev2 = b.get_revision_info(revision[0])[0]
947
elif len(revision) == 2:
948
rev1 = b.get_revision_info(revision[0])[0]
949
rev2 = b.get_revision_info(revision[1])[0]
951
raise BzrCommandError('bzr log --revision takes one or two values.')
958
mutter('encoding log as %r' % bzrlib.user_encoding)
960
# use 'replace' so that we don't abort if trying to write out
961
# in e.g. the default C locale.
962
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
968
lf = log_formatter(log_format,
971
show_timezone=timezone)
561
984
class cmd_touching_revisions(Command):
562
"""Return revision-ids which affected a particular file."""
985
"""Return revision-ids which affected a particular file.
987
A more user-friendly interface is "bzr log FILE"."""
564
989
takes_args = ["filename"]
565
990
def run(self, filename):
566
b = Branch(filename, lock_mode='r')
991
b = find_branch(filename)
567
992
inv = b.read_working_inventory()
568
993
file_id = inv.path2id(b.relpath(filename))
569
994
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
628
1054
def run(self, name_pattern):
629
1055
from bzrlib.atomicfile import AtomicFile
1058
b = find_branch('.')
633
1059
ifn = b.abspath('.bzrignore')
635
# FIXME: probably doesn't handle non-ascii patterns
637
1061
if os.path.exists(ifn):
638
f = b.controlfile(ifn, 'rt')
1064
igns = f.read().decode('utf-8')
1070
# TODO: If the file already uses crlf-style termination, maybe
1071
# we should use that for the newly added lines?
644
1073
if igns and igns[-1] != '\n':
646
1075
igns += name_pattern + '\n'
648
f = AtomicFile(ifn, 'wt')
1078
f = AtomicFile(ifn, 'wt')
1079
f.write(igns.encode('utf-8'))
652
1084
inv = b.working_tree().inventory
653
1085
if inv.path2id('.bzrignore'):
687
1119
except ValueError:
688
1120
raise BzrCommandError("not a valid revision-number: %r" % revno)
690
print Branch('.').lookup_revision(revno)
1122
print find_branch('.').lookup_revision(revno)
693
1125
class cmd_export(Command):
694
1126
"""Export past revision to destination directory.
696
If no revision is specified this exports the last committed revision."""
1128
If no revision is specified this exports the last committed revision.
1130
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1131
given, try to find the format with the extension. If no extension
1132
is found exports to a directory (equivalent to --format=dir).
1134
Root may be the top directory for tar, tgz and tbz2 formats. If none
1135
is given, the top directory will be the root name of the file."""
1136
# TODO: list known exporters
697
1137
takes_args = ['dest']
698
takes_options = ['revision']
699
def run(self, dest, revision=None):
702
rh = b.revision_history()[-1]
1138
takes_options = ['revision', 'format', 'root']
1139
def run(self, dest, revision=None, format=None, root=None):
1141
b = find_branch('.')
1142
if revision is None:
1143
rev_id = b.last_patch()
704
rh = b.lookup_revision(int(revision))
705
t = b.revision_tree(rh)
1145
if len(revision) != 1:
1146
raise BzrError('bzr export --revision takes exactly 1 argument')
1147
revno, rev_id = b.get_revision_info(revision[0])
1148
t = b.revision_tree(rev_id)
1149
root, ext = os.path.splitext(dest)
1151
if ext in (".tar",):
1153
elif ext in (".gz", ".tgz"):
1155
elif ext in (".bz2", ".tbz2"):
1159
t.export(dest, format, root)
709
1162
class cmd_cat(Command):
730
1185
class cmd_commit(Command):
731
1186
"""Commit changes into a new revision.
733
TODO: Commit only selected files.
1188
If selected files are specified, only changes to those files are
1189
committed. If a directory is specified then its contents are also
1192
A selected-file commit may fail in some cases where the committed
1193
tree would be invalid, such as trying to commit a file in a
1194
newly-added directory that is not itself committed.
735
1196
TODO: Run hooks on tree to-be-committed, and after commit.
737
1198
TODO: Strict commit that fails if there are unknown or deleted files.
739
takes_options = ['message', 'file', 'verbose']
1200
takes_args = ['selected*']
1201
takes_options = ['message', 'file', 'verbose', 'unchanged']
740
1202
aliases = ['ci', 'checkin']
742
def run(self, message=None, file=None, verbose=False):
1204
def run(self, message=None, file=None, verbose=True, selected_list=None,
1206
from bzrlib.errors import PointlessCommit
1207
from bzrlib.osutils import get_text_message
743
1209
## Warning: shadows builtin file()
744
1210
if not message and not file:
745
raise BzrCommandError("please specify a commit message",
746
["use either --message or --file"])
1213
catcher = cStringIO.StringIO()
1214
sys.stdout = catcher
1215
cmd_status({"file_list":selected_list}, {})
1216
info = catcher.getvalue()
1218
message = get_text_message(info)
1221
raise BzrCommandError("please specify a commit message",
1222
["use either --message or --file"])
747
1223
elif message and file:
748
1224
raise BzrCommandError("please specify either --message or --file")
760
1246
This command checks various invariants about the branch storage to
761
1247
detect data corruption or bzr bugs.
763
takes_args = ['dir?']
764
def run(self, dir='.'):
766
bzrlib.check.check(Branch(dir, find_root=False))
1249
If given the --update flag, it will update some optional fields
1250
to help ensure data consistency.
1252
takes_args = ['dir?']
1254
def run(self, dir='.'):
1255
from bzrlib.check import check
1256
check(find_branch(dir))
1260
class cmd_scan_cache(Command):
1263
from bzrlib.hashcache import HashCache
1270
print '%6d stats' % c.stat_count
1271
print '%6d in hashcache' % len(c._cache)
1272
print '%6d files removed from cache' % c.removed_count
1273
print '%6d hashes updated' % c.update_count
1274
print '%6d files changed too recently to cache' % c.danger_count
1281
class cmd_upgrade(Command):
1282
"""Upgrade branch storage to current format.
1284
This should normally be used only after the check command tells
1287
takes_args = ['dir?']
1289
def run(self, dir='.'):
1290
from bzrlib.upgrade import upgrade
1291
upgrade(find_branch(dir))
781
1306
class cmd_selftest(Command):
782
1307
"""Run internal test suite"""
785
failures, tests = 0, 0
787
import doctest, bzrlib.store, bzrlib.tests
788
bzrlib.trace.verbose = False
790
for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \
791
bzrlib.tree, bzrlib.tests, bzrlib.commands, bzrlib.add:
792
mf, mt = doctest.testmod(m)
795
print '%-40s %3d tests' % (m.__name__, mt),
797
print '%3d FAILED!' % mf
801
print '%-40s %3d tests' % ('total', tests),
803
print '%3d FAILED!' % failures
1309
takes_options = ['verbose']
1310
def run(self, verbose=False):
1311
from bzrlib.selftest import selftest
1312
return int(not selftest(verbose=verbose))
809
1315
class cmd_version(Command):
810
"""Show version of bzr"""
1316
"""Show version of bzr."""
814
1320
def show_version():
815
1321
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1322
# is bzrlib itself in a branch?
1323
bzrrev = bzrlib.get_bzr_revision()
1325
print " (bzr checkout, revision %d {%s})" % bzrrev
816
1326
print bzrlib.__copyright__
817
1327
print "http://bazaar-ng.org/"
828
1338
print "it sure does!"
1340
def parse_spec(spec):
1342
>>> parse_spec(None)
1344
>>> parse_spec("./")
1346
>>> parse_spec("../@")
1348
>>> parse_spec("../f/@35")
1350
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1351
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1356
parsed = spec.split('/@')
1357
assert len(parsed) == 2
1362
parsed[1] = int(parsed[1])
1364
pass # We can allow stuff like ./@revid:blahblahblah
1366
assert parsed[1] >=0
1368
parsed = [spec, None]
1373
class cmd_merge(Command):
1374
"""Perform a three-way merge of trees.
1376
The SPEC parameters are working tree or revision specifiers. Working trees
1377
are specified using standard paths or urls. No component of a directory
1378
path may begin with '@'.
1380
Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1382
Revisions are specified using a dirname/@revno pair, where dirname is the
1383
branch directory and revno is the revision within that branch. If no revno
1384
is specified, the latest revision is used.
1386
Revision examples: './@127', 'foo/@', '../@1'
1388
The OTHER_SPEC parameter is required. If the BASE_SPEC parameter is
1389
not supplied, the common ancestor of OTHER_SPEC the current branch is used
1392
merge refuses to run if there are any uncommitted changes, unless
1395
takes_args = ['other_spec', 'base_spec?']
1396
takes_options = ['force']
1398
def run(self, other_spec, base_spec=None, force=False):
1399
from bzrlib.merge import merge
1400
merge(parse_spec(other_spec), parse_spec(base_spec),
1401
check_clean=(not force))
1405
class cmd_revert(Command):
1406
"""Restore selected files from a previous revision.
1408
takes_args = ['file+']
1409
def run(self, file_list):
1410
from bzrlib.branch import find_branch
1415
b = find_branch(file_list[0])
1417
b.revert([b.relpath(f) for f in file_list])
1420
class cmd_merge_revert(Command):
1421
"""Reverse all changes since the last commit.
1423
Only versioned files are affected.
1425
TODO: Store backups of any files that will be reverted, so
1426
that the revert can be undone.
1428
takes_options = ['revision']
1430
def run(self, revision=None):
1431
from bzrlib.merge import merge
1432
if revision is None:
1434
elif len(revision) != 1:
1435
raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1436
merge(('.', revision[0]), parse_spec('.'),
831
1441
class cmd_assert_fail(Command):
832
1442
"""Test reporting of assertion failures"""
903
1528
(['status'], {'all': True})
904
1529
>>> parse_args('commit --message=biter'.split())
905
1530
(['commit'], {'message': u'biter'})
1531
>>> parse_args('log -r 500'.split())
1532
(['log'], {'revision': [500]})
1533
>>> parse_args('log -r500..600'.split())
1534
(['log'], {'revision': [500, 600]})
1535
>>> parse_args('log -vr500..600'.split())
1536
(['log'], {'verbose': True, 'revision': [500, 600]})
1537
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
1538
(['log'], {'revision': ['v500', 600]})
924
1557
if optname not in OPTIONS:
925
bailout('unknown long option %r' % a)
1558
raise BzrError('unknown long option %r' % a)
927
1560
shortopt = a[1:]
928
if shortopt not in SHORT_OPTIONS:
929
bailout('unknown short option %r' % a)
930
optname = SHORT_OPTIONS[shortopt]
1561
if shortopt in SHORT_OPTIONS:
1562
# Multi-character options must have a space to delimit
1564
optname = SHORT_OPTIONS[shortopt]
1566
# Single character short options, can be chained,
1567
# and have their value appended to their name
1569
if shortopt not in SHORT_OPTIONS:
1570
# We didn't find the multi-character name, and we
1571
# didn't find the single char name
1572
raise BzrError('unknown short option %r' % a)
1573
optname = SHORT_OPTIONS[shortopt]
1576
# There are extra things on this option
1577
# see if it is the value, or if it is another
1579
optargfn = OPTIONS[optname]
1580
if optargfn is None:
1581
# This option does not take an argument, so the
1582
# next entry is another short option, pack it back
1584
argv.insert(0, '-' + a[2:])
1586
# This option takes an argument, so pack it
932
1590
if optname in opts:
933
1591
# XXX: Do we ever want to support this, e.g. for -r?
934
bailout('repeated option %r' % a)
1592
raise BzrError('repeated option %r' % a)
936
1594
optargfn = OPTIONS[optname]
938
1596
if optarg == None:
940
bailout('option %r needs an argument' % a)
1598
raise BzrError('option %r needs an argument' % a)
942
1600
optarg = argv.pop(0)
943
1601
opts[optname] = optargfn(optarg)
945
1603
if optarg != None:
946
bailout('option %r takes no argument' % optname)
1604
raise BzrError('option %r takes no argument' % optname)
947
1605
opts[optname] = True
1658
def _parse_master_args(argv):
1659
"""Parse the arguments that always go with the original command.
1660
These are things like bzr --no-plugins, etc.
1662
There are now 2 types of option flags. Ones that come *before* the command,
1663
and ones that come *after* the command.
1664
Ones coming *before* the command are applied against all possible commands.
1665
And are generally applied before plugins are loaded.
1667
The current list are:
1668
--builtin Allow plugins to load, but don't let them override builtin commands,
1669
they will still be allowed if they do not override a builtin.
1670
--no-plugins Don't load any plugins. This lets you get back to official source
1672
--profile Enable the hotspot profile before running the command.
1673
For backwards compatibility, this is also a non-master option.
1674
--version Spit out the version of bzr that is running and exit.
1675
This is also a non-master option.
1676
--help Run help and exit, also a non-master option (I think that should stay, though)
1678
>>> argv, opts = _parse_master_args(['--test'])
1679
Traceback (most recent call last):
1681
BzrCommandError: Invalid master option: 'test'
1682
>>> argv, opts = _parse_master_args(['--version', 'command'])
1685
>>> print opts['version']
1687
>>> argv, opts = _parse_master_args(['--profile', 'command', '--more-options'])
1689
['command', '--more-options']
1690
>>> print opts['profile']
1692
>>> argv, opts = _parse_master_args(['--no-plugins', 'command'])
1695
>>> print opts['no-plugins']
1697
>>> print opts['profile']
1699
>>> argv, opts = _parse_master_args(['command', '--profile'])
1701
['command', '--profile']
1702
>>> print opts['profile']
1705
master_opts = {'builtin':False,
1713
if arg[:2] != '--': # at the first non-option, we return the rest
1715
arg = arg[2:] # Remove '--'
1716
if arg not in master_opts:
1717
# We could say that this is not an error, that we should
1718
# just let it be handled by the main section instead
1719
raise BzrCommandError('Invalid master option: %r' % arg)
1720
argv.pop(0) # We are consuming this entry
1721
master_opts[arg] = True
1722
return argv, master_opts
1001
1726
def run_bzr(argv):
1002
1727
"""Execute a command.
1004
1729
This is similar to main(), but without all the trappings for
1005
1730
logging and error handling.
1733
The command-line arguments, without the program name.
1735
Returns a command status or raises an exception.
1007
1737
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1739
# some options like --builtin and --no-plugins have special effects
1740
argv, master_opts = _parse_master_args(argv)
1741
if not master_opts['no-plugins']:
1742
from bzrlib.plugin import load_plugins
1745
args, opts = parse_args(argv)
1747
if master_opts.get('help') or 'help' in opts:
1748
from bzrlib.help import help
1755
if 'version' in opts:
1759
if args and args[0] == 'builtin':
1760
include_plugins=False
1010
args, opts = parse_args(argv[1:])
1018
elif 'version' in opts:
1021
1764
cmd = str(args.pop(0))
1022
1765
except IndexError:
1023
log_error('usage: bzr COMMAND')
1024
log_error(' try "bzr help"')
1766
print >>sys.stderr, "please try 'bzr help' for help"
1027
canonical_cmd, cmd_class = get_cmd_class(cmd)
1769
plugins_override = not (master_opts['builtin'])
1770
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1772
profile = master_opts['profile']
1773
# For backwards compatibility, I would rather stick with --profile being a
1774
# master/global option
1030
1775
if 'profile' in opts:
1032
1777
del opts['profile']
1036
1779
# check options are reasonable
1037
1780
allowed = cmd_class.takes_options