19
import sys, os, time, os.path
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__])
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, \
46
32
def _squish_command_name(cmd):
51
37
assert cmd.startswith("cmd_")
52
38
return cmd[4:].replace('_','-')
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('..'):
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
def _get_cmd_dict(plugins_override=True):
41
"""Return canonical name and class for all registered commands."""
142
42
for k, v in globals().iteritems():
143
43
if k.startswith("cmd_"):
144
d[_unsquish_command_name(k)] = v
145
# If we didn't load plugins, the plugin_cmds dict will be empty
147
d.update(plugin_cmds)
149
d2 = plugin_cmds.copy()
155
def get_all_cmds(plugins_override=True):
156
"""Return canonical name and class for all registered commands."""
157
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
161
def get_cmd_class(cmd, plugins_override=True):
44
yield _unsquish_command_name(k), v
46
def get_cmd_class(cmd):
162
47
"""Return the canonical name and command class for a command.
164
49
cmd = str(cmd) # not unicode
166
51
# first look up this command under the specified name
167
cmds = _get_cmd_dict(plugins_override=plugins_override)
169
return cmd, cmds[cmd]
53
return cmd, globals()[_squish_command_name(cmd)]
173
57
# look for any command which claims this as an alias
174
for cmdname, cmdclass in cmds.iteritems():
58
for cmdname, cmdclass in get_all_cmds():
175
59
if cmd in cmdclass.aliases:
176
60
return cmdname, cmdclass
265
145
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.
268
151
pipe = os.popen('%s --bzr-usage' % path, 'r')
269
152
self.takes_options = pipe.readline().split()
271
for opt in self.takes_options:
272
if not opt in OPTIONS:
273
raise BzrError("Unknown option '%s' returned by external command %s"
276
# TODO: Is there any way to check takes_args is valid here?
277
153
self.takes_args = pipe.readline().split()
279
if pipe.close() is not None:
280
raise BzrError("Failed funning '%s --bzr-usage'" % path)
282
156
pipe = os.popen('%s --bzr-help' % path, 'r')
283
157
self.__doc__ = pipe.read()
284
if pipe.close() is not None:
285
raise BzrError("Failed funning '%s --bzr-help'" % path)
287
160
def __call__(self, options, arguments):
288
161
Command.__init__(self, options, arguments)
317
189
class cmd_status(Command):
318
190
"""Display status summary.
320
This reports on versioned and unknown files, reporting them
321
grouped by state. Possible states are:
324
Versioned in the working copy but not in the previous revision.
327
Versioned in the previous revision but removed or deleted
331
Path of this file changed from the previous revision;
332
the text may also have changed. This includes files whose
333
parent directory was renamed.
336
Text has changed since the previous revision.
339
Nothing about this file has changed since the previous revision.
340
Only shown with --all.
343
Not versioned and not matching an ignore pattern.
345
To see ignored files use 'bzr ignored'. For details in the
346
changes to file texts, use 'bzr diff'.
348
If no arguments are specified, the status of the entire working
349
directory is shown. Otherwise, only the status of the specified
350
files or directories is reported. If a directory is given, status
351
is reported for everything inside that directory.
353
If a revision is specified, the changes since that revision are shown.
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.
355
196
takes_args = ['file*']
356
takes_options = ['all', 'show-ids', 'revision']
197
takes_options = ['all']
357
198
aliases = ['st', 'stat']
359
def run(self, all=False, show_ids=False, file_list=None):
361
b = find_branch(file_list[0])
362
file_list = [b.relpath(x) for x in file_list]
363
# special case: only one path was given and it's the root
365
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)
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)
375
205
class cmd_cat_revision(Command):
426
233
whether already versioned or not, are searched for files or
427
234
subdirectories that are neither versioned or ignored, and these
428
235
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
238
Therefore simply saying 'bzr add .' will version all files that
432
239
are currently unknown.
434
241
TODO: Perhaps adding a file whose directly is not versioned should
435
242
recursively add that parent, rather than giving an error?
437
takes_args = ['file*']
438
takes_options = ['verbose', 'no-recurse']
244
takes_args = ['file+']
245
takes_options = ['verbose']
440
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)
247
def run(self, file_list, verbose=False):
248
bzrlib.add.smart_add(file_list, verbose)
463
251
class cmd_relpath(Command):
464
252
"""Show path of a file relative to root"""
465
253
takes_args = ['filename']
468
255
def run(self, filename):
469
print find_branch(filename).relpath(filename)
256
print Branch(filename).relpath(filename)
473
260
class cmd_inventory(Command):
474
261
"""Show inventory of the current working copy or a revision."""
475
takes_options = ['revision', 'show-ids']
262
takes_options = ['revision']
477
def run(self, revision=None, show_ids=False):
264
def run(self, revision=None):
479
266
if revision == None:
480
267
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]))
269
inv = b.get_revision_inventory(b.lookup_revision(revision))
487
for path, entry in inv.entries():
489
print '%-50s %s' % (path, entry.file_id)
271
for path, entry in inv.iter_entries():
272
print '%-50s %s' % (entry.file_id, path)
494
275
class cmd_move(Command):
523
304
takes_args = ['from_name', 'to_name']
525
306
def run(self, from_name, to_name):
527
308
b.rename_one(b.relpath(from_name), b.relpath(to_name))
533
class cmd_pull(Command):
534
"""Pull any changes from another branch into the current one.
536
If the location is omitted, the last-used location will be used.
537
Both the revision history and the working directory will be
540
This command only works on branches that have not diverged. Branches are
541
considered diverged if both branches have had commits without first
542
pulling from the other.
544
If branches have diverged, you can use 'bzr merge' to pull the text changes
545
from one into the other.
547
takes_args = ['location?']
549
def run(self, location=None):
550
from bzrlib.merge import merge
552
from shutil import rmtree
555
br_to = find_branch('.')
558
stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
560
if e.errno != errno.ENOENT:
563
if stored_loc is None:
564
raise BzrCommandError("No pull location known or specified.")
566
print "Using last location: %s" % stored_loc
567
location = stored_loc
568
cache_root = tempfile.mkdtemp()
569
from bzrlib.branch import DivergedBranches
570
br_from = find_branch(location)
571
location = pull_loc(br_from)
572
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")
592
class cmd_branch(Command):
593
"""Create a new copy of a branch.
595
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
596
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
598
To retrieve the branch as of a particular revision, supply the --revision
599
parameter, as in "branch foo/bar -r 5".
601
takes_args = ['from_location', 'to_location?']
602
takes_options = ['revision']
604
def run(self, from_location, to_location=None, revision=None):
606
from bzrlib.merge import merge
607
from bzrlib.branch import DivergedBranches, NoSuchRevision, \
608
find_cached_branch, Branch
609
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")
668
def pull_loc(branch):
669
# TODO: Should perhaps just make attribute be 'base' in
670
# RemoteBranch and Branch?
671
if hasattr(branch, "baseurl"):
672
return branch.baseurl
678
312
class cmd_renames(Command):
679
313
"""Show list of renamed files.
747
377
takes_args = ['filename']
748
378
def run(self, filename):
749
b = find_branch(filename)
750
380
inv = b.inventory
751
381
fid = inv.path2id(b.relpath(filename))
753
raise BzrError("%r is not a versioned file" % filename)
383
bailout("%r is not a versioned file" % filename)
754
384
for fip in inv.get_idpath(fid):
758
388
class cmd_revision_history(Command):
759
389
"""Display list of revision ids on this branch."""
762
for patchid in find_branch('.').revision_history():
391
for patchid in Branch('.').revision_history():
766
395
class cmd_directories(Command):
767
396
"""Display list of versioned directories in this branch."""
769
for name, ie in find_branch('.').read_working_inventory().directories():
398
for name, ie in Branch('.').read_working_inventory().directories():
818
446
takes_args = ['file*']
819
takes_options = ['revision', 'diff-options']
820
aliases = ['di', 'dif']
447
takes_options = ['revision']
822
def run(self, revision=None, file_list=None, diff_options=None):
450
def run(self, revision=None, file_list=None):
823
451
from bzrlib.diff import show_diff
826
b = find_branch(file_list[0])
827
file_list = [b.relpath(f) for f in file_list]
828
if file_list == ['']:
829
# 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,
842
external_diff_options=diff_options)
453
show_diff(Branch('.'), revision, file_list)
872
483
"""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:
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)
915
540
class cmd_log(Command):
916
541
"""Show log of this branch.
918
To request a range of logs, you can use the command -r begin:end
919
-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
TODO: Make --revision support uuid: and hash: [future tag:] notation.
543
TODO: Option to limit range.
545
TODO: Perhaps show most-recent first with an option for last.
929
547
takes_args = ['filename?']
930
takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
932
def run(self, filename=None, timezone='original',
939
from bzrlib.branch import find_branch
940
from bzrlib.log import log_formatter, show_log
943
direction = (forward and 'forward') or 'reverse'
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')
946
b = find_branch(filename)
947
fp = b.relpath(filename)
949
file_id = b.read_working_inventory().path2id(fp)
951
file_id = None # points to branch root
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]
965
raise BzrCommandError('bzr log --revision takes one or two values.')
972
mutter('encoding log as %r' % bzrlib.user_encoding)
974
# use 'replace' so that we don't abort if trying to write out
975
# in e.g. the default C locale.
976
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
982
lf = log_formatter(log_format,
985
show_timezone=timezone)
553
filename = b.relpath(filename)
554
bzrlib.show_log(b, filename,
555
show_timezone=timezone,
998
561
class cmd_touching_revisions(Command):
999
"""Return revision-ids which affected a particular file.
1001
A more user-friendly interface is "bzr log FILE"."""
562
"""Return revision-ids which affected a particular file."""
1003
564
takes_args = ["filename"]
1004
565
def run(self, filename):
1005
b = find_branch(filename)
566
b = Branch(filename, lock_mode='r')
1006
567
inv = b.read_working_inventory()
1007
568
file_id = inv.path2id(b.relpath(filename))
1008
569
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1068
628
def run(self, name_pattern):
1069
629
from bzrlib.atomicfile import AtomicFile
1072
b = find_branch('.')
1073
633
ifn = b.abspath('.bzrignore')
635
# FIXME: probably doesn't handle non-ascii patterns
1075
637
if os.path.exists(ifn):
1078
igns = f.read().decode('utf-8')
638
f = b.controlfile(ifn, 'rt')
1084
# TODO: If the file already uses crlf-style termination, maybe
1085
# we should use that for the newly added lines?
1087
644
if igns and igns[-1] != '\n':
1089
646
igns += name_pattern + '\n'
1092
f = AtomicFile(ifn, 'wt')
1093
f.write(igns.encode('utf-8'))
648
f = AtomicFile(ifn, 'wt')
1098
652
inv = b.working_tree().inventory
1099
653
if inv.path2id('.bzrignore'):
1133
687
except ValueError:
1134
688
raise BzrCommandError("not a valid revision-number: %r" % revno)
1136
print find_branch('.').lookup_revision(revno)
690
print Branch('.').lookup_revision(revno)
1139
693
class cmd_export(Command):
1140
694
"""Export past revision to destination directory.
1142
If no revision is specified this exports the last committed revision.
1144
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."""
1150
# TODO: list known exporters
696
If no revision is specified this exports the last committed revision."""
1151
697
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()
698
takes_options = ['revision']
699
def run(self, dest, revision=None):
702
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)
704
rh = b.lookup_revision(int(revision))
705
t = b.revision_tree(rh)
1176
709
class cmd_cat(Command):
1199
730
class cmd_commit(Command):
1200
731
"""Commit changes into a new revision.
1202
If selected files are specified, only changes to those files are
1203
committed. If a directory is specified then its contents are also
1206
A selected-file commit may fail in some cases where the committed
1207
tree would be invalid, such as trying to commit a file in a
1208
newly-added directory that is not itself committed.
733
TODO: Commit only selected files.
1210
735
TODO: Run hooks on tree to-be-committed, and after commit.
1212
737
TODO: Strict commit that fails if there are unknown or deleted files.
1214
takes_args = ['selected*']
1215
takes_options = ['message', 'file', 'verbose', 'unchanged']
739
takes_options = ['message', 'file', 'verbose']
1216
740
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
742
def run(self, message=None, file=None, verbose=False):
1223
743
## Warning: shadows builtin file()
1224
744
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"])
745
raise BzrCommandError("please specify a commit message",
746
["use either --message or --file"])
1237
747
elif message and file:
1238
748
raise BzrCommandError("please specify either --message or --file")
1260
760
This command checks various invariants about the branch storage to
1261
761
detect data corruption or bzr bugs.
1263
If given the --update flag, it will update some optional fields
1264
to help ensure data consistency.
1266
takes_args = ['dir?']
1268
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
1295
class cmd_upgrade(Command):
1296
"""Upgrade branch storage to current format.
1298
This should normally be used only after the check command tells
1301
takes_args = ['dir?']
1303
def run(self, dir='.'):
1304
from bzrlib.upgrade import upgrade
1305
upgrade(find_branch(dir))
763
takes_args = ['dir?']
764
def run(self, dir='.'):
766
bzrlib.check.check(Branch(dir, find_root=False))
1320
781
class cmd_selftest(Command):
1321
782
"""Run internal test suite"""
1323
takes_options = ['verbose']
1324
def run(self, verbose=False):
1325
from bzrlib.selftest import selftest
1326
return int(not selftest(verbose=verbose))
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
1329
809
class cmd_version(Command):
1330
"""Show version of bzr."""
810
"""Show version of bzr"""
1334
814
def show_version():
1335
815
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1336
# is bzrlib itself in a branch?
1337
bzrrev = bzrlib.get_bzr_revision()
1339
print " (bzr checkout, revision %d {%s})" % bzrrev
1340
816
print bzrlib.__copyright__
1341
817
print "http://bazaar-ng.org/"
1352
828
print "it sure does!"
1354
def parse_spec(spec):
1356
>>> parse_spec(None)
1358
>>> parse_spec("./")
1360
>>> parse_spec("../@")
1362
>>> parse_spec("../f/@35")
1364
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
1365
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1370
parsed = spec.split('/@')
1371
assert len(parsed) == 2
1376
parsed[1] = int(parsed[1])
1378
pass # We can allow stuff like ./@revid:blahblahblah
1380
assert parsed[1] >=0
1382
parsed = [spec, None]
1387
class cmd_merge(Command):
1388
"""Perform a three-way merge of trees.
1390
The SPEC parameters are working tree or revision specifiers. Working trees
1391
are specified using standard paths or urls. No component of a directory
1392
path may begin with '@'.
1394
Working tree examples: '.', '..', 'foo@', but NOT 'foo/@bar'
1396
Revisions are specified using a dirname/@revno pair, where dirname is the
1397
branch directory and revno is the revision within that branch. If no revno
1398
is specified, the latest revision is used.
1400
Revision examples: './@127', 'foo/@', '../@1'
1402
The OTHER_SPEC parameter is required. If the BASE_SPEC parameter is
1403
not supplied, the common ancestor of OTHER_SPEC the current branch is used
1406
merge refuses to run if there are any uncommitted changes, unless
1409
takes_args = ['other_spec', 'base_spec?']
1410
takes_options = ['force', 'merge-type']
1412
def run(self, other_spec, base_spec=None, force=False, merge_type=None):
1413
from bzrlib.merge import merge
1414
from bzrlib.merge_core import ApplyMerge3
1415
if merge_type is None:
1416
merge_type = ApplyMerge3
1417
merge(parse_spec(other_spec), parse_spec(base_spec),
1418
check_clean=(not force), merge_type=merge_type)
1422
class cmd_revert(Command):
1423
"""Restore selected files from a previous revision.
1425
takes_args = ['file+']
1426
def run(self, file_list):
1427
from bzrlib.branch import find_branch
1432
b = find_branch(file_list[0])
1434
b.revert([b.relpath(f) for f in file_list])
1437
class cmd_merge_revert(Command):
1438
"""Reverse all changes since the last commit.
1440
Only versioned files are affected. By default, any files that are changed
1441
will be backed up first. Backup files have a '~' appended to their name.
1443
takes_options = ['revision', 'no-backup']
1445
def run(self, revision=None, no_backup=False):
1446
from bzrlib.merge import merge
1447
if revision is None:
1449
elif len(revision) != 1:
1450
raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
1451
merge(('.', revision[0]), parse_spec('.'),
1454
backup_files=not no_backup)
1457
831
class cmd_assert_fail(Command):
1458
832
"""Test reporting of assertion failures"""
1546
903
(['status'], {'all': True})
1547
904
>>> parse_args('commit --message=biter'.split())
1548
905
(['commit'], {'message': u'biter'})
1549
>>> parse_args('log -r 500'.split())
1550
(['log'], {'revision': [500]})
1551
>>> parse_args('log -r500..600'.split())
1552
(['log'], {'revision': [500, 600]})
1553
>>> parse_args('log -vr500..600'.split())
1554
(['log'], {'verbose': True, 'revision': [500, 600]})
1555
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
1556
(['log'], {'revision': ['v500', 600]})
1575
924
if optname not in OPTIONS:
1576
raise BzrError('unknown long option %r' % a)
925
bailout('unknown long option %r' % a)
1578
927
shortopt = a[1:]
1579
if shortopt in SHORT_OPTIONS:
1580
# Multi-character options must have a space to delimit
1582
optname = SHORT_OPTIONS[shortopt]
1584
# Single character short options, can be chained,
1585
# and have their value appended to their name
1587
if shortopt not in SHORT_OPTIONS:
1588
# We didn't find the multi-character name, and we
1589
# didn't find the single char name
1590
raise BzrError('unknown short option %r' % a)
1591
optname = SHORT_OPTIONS[shortopt]
1594
# There are extra things on this option
1595
# see if it is the value, or if it is another
1597
optargfn = OPTIONS[optname]
1598
if optargfn is None:
1599
# This option does not take an argument, so the
1600
# next entry is another short option, pack it back
1602
argv.insert(0, '-' + a[2:])
1604
# This option takes an argument, so pack it
928
if shortopt not in SHORT_OPTIONS:
929
bailout('unknown short option %r' % a)
930
optname = SHORT_OPTIONS[shortopt]
1608
932
if optname in opts:
1609
933
# XXX: Do we ever want to support this, e.g. for -r?
1610
raise BzrError('repeated option %r' % a)
934
bailout('repeated option %r' % a)
1612
936
optargfn = OPTIONS[optname]
1614
938
if optarg == None:
1616
raise BzrError('option %r needs an argument' % a)
940
bailout('option %r needs an argument' % a)
1618
942
optarg = argv.pop(0)
1619
943
opts[optname] = optargfn(optarg)
1621
945
if optarg != None:
1622
raise BzrError('option %r takes no argument' % optname)
946
bailout('option %r takes no argument' % optname)
1623
947
opts[optname] = True
1676
def _parse_master_args(argv):
1677
"""Parse the arguments that always go with the original command.
1678
These are things like bzr --no-plugins, etc.
1680
There are now 2 types of option flags. Ones that come *before* the command,
1681
and ones that come *after* the command.
1682
Ones coming *before* the command are applied against all possible commands.
1683
And are generally applied before plugins are loaded.
1685
The current list are:
1686
--builtin Allow plugins to load, but don't let them override builtin commands,
1687
they will still be allowed if they do not override a builtin.
1688
--no-plugins Don't load any plugins. This lets you get back to official source
1690
--profile Enable the hotspot profile before running the command.
1691
For backwards compatibility, this is also a non-master option.
1692
--version Spit out the version of bzr that is running and exit.
1693
This is also a non-master option.
1694
--help Run help and exit, also a non-master option (I think that should stay, though)
1696
>>> argv, opts = _parse_master_args(['--test'])
1697
Traceback (most recent call last):
1699
BzrCommandError: Invalid master option: 'test'
1700
>>> argv, opts = _parse_master_args(['--version', 'command'])
1703
>>> print opts['version']
1705
>>> argv, opts = _parse_master_args(['--profile', 'command', '--more-options'])
1707
['command', '--more-options']
1708
>>> print opts['profile']
1710
>>> argv, opts = _parse_master_args(['--no-plugins', 'command'])
1713
>>> print opts['no-plugins']
1715
>>> print opts['profile']
1717
>>> argv, opts = _parse_master_args(['command', '--profile'])
1719
['command', '--profile']
1720
>>> print opts['profile']
1723
master_opts = {'builtin':False,
1731
if arg[:2] != '--': # at the first non-option, we return the rest
1733
arg = arg[2:] # Remove '--'
1734
if arg not in master_opts:
1735
# We could say that this is not an error, that we should
1736
# just let it be handled by the main section instead
1737
raise BzrCommandError('Invalid master option: %r' % arg)
1738
argv.pop(0) # We are consuming this entry
1739
master_opts[arg] = True
1740
return argv, master_opts
1744
1001
def run_bzr(argv):
1745
1002
"""Execute a command.
1747
1004
This is similar to main(), but without all the trappings for
1748
1005
logging and error handling.
1751
The command-line arguments, without the program name.
1753
Returns a command status or raises an exception.
1755
1007
argv = [a.decode(bzrlib.user_encoding) for a in argv]
1757
# some options like --builtin and --no-plugins have special effects
1758
argv, master_opts = _parse_master_args(argv)
1759
if not master_opts['no-plugins']:
1760
from bzrlib.plugin import load_plugins
1763
args, opts = parse_args(argv)
1765
if master_opts.get('help') or 'help' in opts:
1766
from bzrlib.help import help
1773
if 'version' in opts:
1777
if args and args[0] == 'builtin':
1778
include_plugins=False
1010
args, opts = parse_args(argv[1:])
1018
elif 'version' in opts:
1782
1021
cmd = str(args.pop(0))
1783
1022
except IndexError:
1784
print >>sys.stderr, "please try 'bzr help' for help"
1023
log_error('usage: bzr COMMAND')
1024
log_error(' try "bzr help"')
1787
plugins_override = not (master_opts['builtin'])
1788
canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1027
canonical_cmd, cmd_class = get_cmd_class(cmd)
1790
profile = master_opts['profile']
1791
# For backwards compatibility, I would rather stick with --profile being a
1792
# master/global option
1793
1030
if 'profile' in opts:
1795
1032
del opts['profile']
1797
1036
# check options are reasonable
1798
1037
allowed = cmd_class.takes_options