1
# Copyright (C) 2004, 2005 by Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# DO NOT change this to cStringIO - it results in control files
19
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
21
from StringIO import StringIO
26
from bzrlib import BZRDIR
27
from bzrlib.commands import Command, display_command
28
from bzrlib.branch import Branch
29
from bzrlib.revision import common_ancestor
30
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
31
NotBranchError, DivergedBranches, NotConflicted,
32
NoSuchFile, NoWorkingTree, FileInWrongBranch)
33
from bzrlib.option import Option
34
from bzrlib.revisionspec import RevisionSpec
36
from bzrlib.trace import mutter, note, log_error, warning
37
from bzrlib.workingtree import WorkingTree
40
def branch_files(file_list, default_branch='.'):
42
return inner_branch_files(file_list, default_branch)
43
except FileInWrongBranch, e:
45
raise BzrCommandError("%s is not in the same branch as %s" %
46
(e.path, file_list[0]))
48
def inner_branch_files(file_list, default_branch='.'):
50
Return a branch and list of branch-relative paths.
51
If supplied file_list is empty or None, the branch default will be used,
52
and returned file_list will match the original.
54
if file_list is None or len(file_list) == 0:
55
return Branch.open_containing(default_branch)[0], file_list
56
b = Branch.open_containing(file_list[0])[0]
58
# note that if this is a remote branch, we would want
59
# relpath against the transport. RBC 20051018
60
# Most branch ops can't meaningfully operate on files in remote branches;
61
# the above comment was in cmd_status. ADHB 20051026
62
tree = WorkingTree(b.base, b)
64
for filename in file_list:
66
new_list.append(tree.relpath(filename))
67
except NotBranchError:
68
raise FileInWrongBranch(b, filename)
72
# TODO: Make sure no commands unconditionally use the working directory as a
73
# branch. If a filename argument is used, the first of them should be used to
74
# specify the branch. (Perhaps this can be factored out into some kind of
75
# Argument class, representing a file in a branch, where the first occurrence
78
class cmd_status(Command):
79
"""Display status summary.
81
This reports on versioned and unknown files, reporting them
82
grouped by state. Possible states are:
85
Versioned in the working copy but not in the previous revision.
88
Versioned in the previous revision but removed or deleted
92
Path of this file changed from the previous revision;
93
the text may also have changed. This includes files whose
94
parent directory was renamed.
97
Text has changed since the previous revision.
100
Nothing about this file has changed since the previous revision.
101
Only shown with --all.
104
Not versioned and not matching an ignore pattern.
106
To see ignored files use 'bzr ignored'. For details in the
107
changes to file texts, use 'bzr diff'.
109
If no arguments are specified, the status of the entire working
110
directory is shown. Otherwise, only the status of the specified
111
files or directories is reported. If a directory is given, status
112
is reported for everything inside that directory.
114
If a revision argument is given, the status is calculated against
115
that revision, or between two revisions if two are provided.
118
# TODO: --no-recurse, --recurse options
120
takes_args = ['file*']
121
takes_options = ['all', 'show-ids', 'revision']
122
aliases = ['st', 'stat']
125
def run(self, all=False, show_ids=False, file_list=None, revision=None):
126
b, file_list = branch_files(file_list)
128
from bzrlib.status import show_status
129
show_status(b, show_unchanged=all, show_ids=show_ids,
130
specific_files=file_list, revision=revision)
133
class cmd_cat_revision(Command):
134
"""Write out metadata for a revision.
136
The revision to print can either be specified by a specific
137
revision identifier, or you can use --revision.
141
takes_args = ['revision_id?']
142
takes_options = ['revision']
145
def run(self, revision_id=None, revision=None):
147
if revision_id is not None and revision is not None:
148
raise BzrCommandError('You can only supply one of revision_id or --revision')
149
if revision_id is None and revision is None:
150
raise BzrCommandError('You must supply either --revision or a revision_id')
151
b = Branch.open_containing('.')[0]
152
if revision_id is not None:
153
sys.stdout.write(b.get_revision_xml_file(revision_id).read())
154
elif revision is not None:
157
raise BzrCommandError('You cannot specify a NULL revision.')
158
revno, rev_id = rev.in_history(b)
159
sys.stdout.write(b.get_revision_xml_file(rev_id).read())
162
class cmd_revno(Command):
163
"""Show current revision number.
165
This is equal to the number of revisions on this branch."""
168
print Branch.open_containing('.')[0].revno()
171
class cmd_revision_info(Command):
172
"""Show revision number and revision id for a given revision identifier.
175
takes_args = ['revision_info*']
176
takes_options = ['revision']
178
def run(self, revision=None, revision_info_list=[]):
181
if revision is not None:
182
revs.extend(revision)
183
if revision_info_list is not None:
184
for rev in revision_info_list:
185
revs.append(RevisionSpec(rev))
187
raise BzrCommandError('You must supply a revision identifier')
189
b = Branch.open_containing('.')[0]
192
revinfo = rev.in_history(b)
193
if revinfo.revno is None:
194
print ' %s' % revinfo.rev_id
196
print '%4d %s' % (revinfo.revno, revinfo.rev_id)
199
class cmd_add(Command):
200
"""Add specified files or directories.
202
In non-recursive mode, all the named items are added, regardless
203
of whether they were previously ignored. A warning is given if
204
any of the named files are already versioned.
206
In recursive mode (the default), files are treated the same way
207
but the behaviour for directories is different. Directories that
208
are already versioned do not give a warning. All directories,
209
whether already versioned or not, are searched for files or
210
subdirectories that are neither versioned or ignored, and these
211
are added. This search proceeds recursively into versioned
212
directories. If no names are given '.' is assumed.
214
Therefore simply saying 'bzr add' will version all files that
215
are currently unknown.
217
Adding a file whose parent directory is not versioned will
218
implicitly add the parent, and so on up to the root. This means
219
you should never need to explictly add a directory, they'll just
220
get added when you add a file in the directory.
222
takes_args = ['file*']
223
takes_options = ['no-recurse', 'quiet']
225
def run(self, file_list, no_recurse=False, quiet=False):
226
from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
228
reporter = add_reporter_null
230
reporter = add_reporter_print
231
smart_add(file_list, not no_recurse, reporter)
234
class cmd_mkdir(Command):
235
"""Create a new versioned directory.
237
This is equivalent to creating the directory and then adding it.
239
takes_args = ['dir+']
241
def run(self, dir_list):
246
b, dd = Branch.open_containing(d)
251
class cmd_relpath(Command):
252
"""Show path of a file relative to root"""
253
takes_args = ['filename']
257
def run(self, filename):
258
branch, relpath = Branch.open_containing(filename)
262
class cmd_inventory(Command):
263
"""Show inventory of the current working copy or a revision."""
264
takes_options = ['revision', 'show-ids']
267
def run(self, revision=None, show_ids=False):
268
b = Branch.open_containing('.')[0]
270
inv = b.working_tree().read_working_inventory()
272
if len(revision) > 1:
273
raise BzrCommandError('bzr inventory --revision takes'
274
' exactly one revision identifier')
275
inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
277
for path, entry in inv.entries():
279
print '%-50s %s' % (path, entry.file_id)
284
class cmd_move(Command):
285
"""Move files to a different directory.
290
The destination must be a versioned directory in the same branch.
292
takes_args = ['source$', 'dest']
293
def run(self, source_list, dest):
294
b, source_list = branch_files(source_list)
296
# TODO: glob expansion on windows?
297
tree = WorkingTree(b.base, b)
298
b.move(source_list, tree.relpath(dest))
301
class cmd_rename(Command):
302
"""Change the name of an entry.
305
bzr rename frob.c frobber.c
306
bzr rename src/frob.c lib/frob.c
308
It is an error if the destination name exists.
310
See also the 'move' command, which moves files into a different
311
directory without changing their name.
313
# TODO: Some way to rename multiple files without invoking
314
# bzr for each one?"""
315
takes_args = ['from_name', 'to_name']
317
def run(self, from_name, to_name):
318
b, (from_name, to_name) = branch_files((from_name, to_name))
319
b.rename_one(from_name, to_name)
322
class cmd_mv(Command):
323
"""Move or rename a file.
326
bzr mv OLDNAME NEWNAME
327
bzr mv SOURCE... DESTINATION
329
If the last argument is a versioned directory, all the other names
330
are moved into it. Otherwise, there must be exactly two arguments
331
and the file is changed to a new name, which must not already exist.
333
Files cannot be moved between branches.
335
takes_args = ['names*']
336
def run(self, names_list):
337
if len(names_list) < 2:
338
raise BzrCommandError("missing file argument")
339
b, rel_names = branch_files(names_list)
341
if os.path.isdir(names_list[-1]):
342
# move into existing directory
343
for pair in b.move(rel_names[:-1], rel_names[-1]):
344
print "%s => %s" % pair
346
if len(names_list) != 2:
347
raise BzrCommandError('to mv multiple files the destination '
348
'must be a versioned directory')
349
b.rename_one(rel_names[0], rel_names[1])
350
print "%s => %s" % (rel_names[0], rel_names[1])
353
class cmd_pull(Command):
354
"""Pull any changes from another branch into the current one.
356
If there is no default location set, the first pull will set it. After
357
that, you can omit the location to use the default. To change the
358
default, use --remember.
360
This command only works on branches that have not diverged. Branches are
361
considered diverged if both branches have had commits without first
362
pulling from the other.
364
If branches have diverged, you can use 'bzr merge' to pull the text changes
365
from one into the other. Once one branch has merged, the other should
366
be able to pull it again.
368
If you want to forget your local changes and just update your branch to
369
match the remote one, use --overwrite.
371
takes_options = ['remember', 'overwrite', 'verbose']
372
takes_args = ['location?']
374
def run(self, location=None, remember=False, overwrite=False, verbose=False):
375
from bzrlib.merge import merge
376
from shutil import rmtree
379
br_to = Branch.open_containing('.')[0]
380
stored_loc = br_to.get_parent()
382
if stored_loc is None:
383
raise BzrCommandError("No pull location known or specified.")
385
print "Using saved location: %s" % stored_loc
386
location = stored_loc
387
br_from = Branch.open(location)
389
old_rh = br_to.revision_history()
390
br_to.working_tree().pull(br_from, overwrite)
391
except DivergedBranches:
392
raise BzrCommandError("These branches have diverged."
394
if br_to.get_parent() is None or remember:
395
br_to.set_parent(location)
398
new_rh = br_to.revision_history()
401
from bzrlib.log import show_changed_revisions
402
show_changed_revisions(br_to, old_rh, new_rh)
405
class cmd_push(Command):
406
"""Push this branch into another branch.
408
The remote branch will not have its working tree populated because this
409
is both expensive, and may not be supported on the remote file system.
411
Some smart servers or protocols *may* put the working tree in place.
413
If there is no default push location set, the first push will set it.
414
After that, you can omit the location to use the default. To change the
415
default, use --remember.
417
This command only works on branches that have not diverged. Branches are
418
considered diverged if the branch being pushed to is not an older version
421
If branches have diverged, you can use 'bzr push --overwrite' to replace
422
the other branch completely.
424
If you want to ensure you have the different changes in the other branch,
425
do a merge (see bzr help merge) from the other branch, and commit that
426
before doing a 'push --overwrite'.
428
takes_options = ['remember', 'overwrite',
429
Option('create-prefix',
430
help='Create the path leading up to the branch '
431
'if it does not already exist')]
432
takes_args = ['location?']
434
def run(self, location=None, remember=False, overwrite=False,
435
create_prefix=False, verbose=False):
437
from shutil import rmtree
438
from bzrlib.transport import get_transport
440
br_from = Branch.open_containing('.')[0]
441
stored_loc = br_from.get_push_location()
443
if stored_loc is None:
444
raise BzrCommandError("No push location known or specified.")
446
print "Using saved location: %s" % stored_loc
447
location = stored_loc
449
br_to = Branch.open(location)
450
except NotBranchError:
452
transport = get_transport(location).clone('..')
453
if not create_prefix:
455
transport.mkdir(transport.relpath(location))
457
raise BzrCommandError("Parent directory of %s "
458
"does not exist." % location)
460
current = transport.base
461
needed = [(transport, transport.relpath(location))]
464
transport, relpath = needed[-1]
465
transport.mkdir(relpath)
468
new_transport = transport.clone('..')
469
needed.append((new_transport,
470
new_transport.relpath(transport.base)))
471
if new_transport.base == transport.base:
472
raise BzrCommandError("Could not creeate "
476
br_to = Branch.initialize(location)
478
old_rh = br_to.revision_history()
479
br_to.pull(br_from, overwrite)
480
except DivergedBranches:
481
raise BzrCommandError("These branches have diverged."
482
" Try a merge then push with overwrite.")
483
if br_from.get_push_location() is None or remember:
484
br_from.set_push_location(location)
487
new_rh = br_to.revision_history()
490
from bzrlib.log import show_changed_revisions
491
show_changed_revisions(br_to, old_rh, new_rh)
493
class cmd_branch(Command):
494
"""Create a new copy of a branch.
496
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
497
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
499
To retrieve the branch as of a particular revision, supply the --revision
500
parameter, as in "branch foo/bar -r 5".
502
--basis is to speed up branching from remote branches. When specified, it
503
copies all the file-contents, inventory and revision data from the basis
504
branch before copying anything from the remote branch.
506
takes_args = ['from_location', 'to_location?']
507
takes_options = ['revision', 'basis']
508
aliases = ['get', 'clone']
510
def run(self, from_location, to_location=None, revision=None, basis=None):
511
from bzrlib.clone import copy_branch
513
from shutil import rmtree
516
elif len(revision) > 1:
517
raise BzrCommandError(
518
'bzr branch --revision takes exactly 1 revision value')
520
br_from = Branch.open(from_location)
522
if e.errno == errno.ENOENT:
523
raise BzrCommandError('Source location "%s" does not'
524
' exist.' % to_location)
529
if basis is not None:
530
basis_branch = Branch.open_containing(basis)[0]
533
if len(revision) == 1 and revision[0] is not None:
534
revision_id = revision[0].in_history(br_from)[1]
537
if to_location is None:
538
to_location = os.path.basename(from_location.rstrip("/\\"))
541
name = os.path.basename(to_location) + '\n'
543
os.mkdir(to_location)
545
if e.errno == errno.EEXIST:
546
raise BzrCommandError('Target directory "%s" already'
547
' exists.' % to_location)
548
if e.errno == errno.ENOENT:
549
raise BzrCommandError('Parent of "%s" does not exist.' %
554
copy_branch(br_from, to_location, revision_id, basis_branch)
555
except bzrlib.errors.NoSuchRevision:
557
msg = "The branch %s has no revision %s." % (from_location, revision[0])
558
raise BzrCommandError(msg)
559
except bzrlib.errors.UnlistableBranch:
561
msg = "The branch %s cannot be used as a --basis"
562
raise BzrCommandError(msg)
564
branch = Branch.open(to_location)
565
name = StringIO(name)
566
branch.put_controlfile('branch-name', name)
571
class cmd_renames(Command):
572
"""Show list of renamed files.
574
# TODO: Option to show renames between two historical versions.
576
# TODO: Only show renames under dir, rather than in the whole branch.
577
takes_args = ['dir?']
580
def run(self, dir='.'):
581
b = Branch.open_containing(dir)[0]
582
old_inv = b.basis_tree().inventory
583
new_inv = b.working_tree().read_working_inventory()
585
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
587
for old_name, new_name in renames:
588
print "%s => %s" % (old_name, new_name)
591
class cmd_info(Command):
592
"""Show statistical information about a branch."""
593
takes_args = ['branch?']
596
def run(self, branch=None):
598
b = Branch.open_containing(branch)[0]
602
class cmd_remove(Command):
603
"""Make a file unversioned.
605
This makes bzr stop tracking changes to a versioned file. It does
606
not delete the working copy.
608
takes_args = ['file+']
609
takes_options = ['verbose']
612
def run(self, file_list, verbose=False):
613
b, file_list = branch_files(file_list)
614
tree = b.working_tree()
615
tree.remove(file_list, verbose=verbose)
618
class cmd_file_id(Command):
619
"""Print file_id of a particular file or directory.
621
The file_id is assigned when the file is first added and remains the
622
same through all revisions where the file exists, even when it is
626
takes_args = ['filename']
628
def run(self, filename):
629
b, relpath = Branch.open_containing(filename)
630
i = b.working_tree().inventory.path2id(relpath)
632
raise BzrError("%r is not a versioned file" % filename)
637
class cmd_file_path(Command):
638
"""Print path of file_ids to a file or directory.
640
This prints one line for each directory down to the target,
641
starting at the branch root."""
643
takes_args = ['filename']
645
def run(self, filename):
646
b, relpath = Branch.open_containing(filename)
648
fid = inv.path2id(relpath)
650
raise BzrError("%r is not a versioned file" % filename)
651
for fip in inv.get_idpath(fid):
655
class cmd_revision_history(Command):
656
"""Display list of revision ids on this branch."""
660
for patchid in Branch.open_containing('.')[0].revision_history():
664
class cmd_ancestry(Command):
665
"""List all revisions merged into this branch."""
669
b = Branch.open_containing('.')[0]
670
for revision_id in b.get_ancestry(b.last_revision()):
674
class cmd_directories(Command):
675
"""Display list of versioned directories in this branch."""
678
for name, ie in (Branch.open_containing('.')[0].working_tree().
679
read_working_inventory().directories()):
686
class cmd_init(Command):
687
"""Make a directory into a versioned branch.
689
Use this to create an empty branch, or before importing an
692
Recipe for importing a tree of files:
697
bzr commit -m 'imported project'
699
takes_args = ['location?']
700
def run(self, location=None):
701
from bzrlib.branch import Branch
705
# The path has to exist to initialize a
706
# branch inside of it.
707
# Just using os.mkdir, since I don't
708
# believe that we want to create a bunch of
709
# locations if the user supplies an extended path
710
if not os.path.exists(location):
712
Branch.initialize(location)
715
class cmd_diff(Command):
716
"""Show differences in working tree.
718
If files are listed, only the changes in those files are listed.
719
Otherwise, all changes for the tree are listed.
726
# TODO: Allow diff across branches.
727
# TODO: Option to use external diff command; could be GNU diff, wdiff,
728
# or a graphical diff.
730
# TODO: Python difflib is not exactly the same as unidiff; should
731
# either fix it up or prefer to use an external diff.
733
# TODO: If a directory is given, diff everything under that.
735
# TODO: Selected-file diff is inefficient and doesn't show you
738
# TODO: This probably handles non-Unix newlines poorly.
740
takes_args = ['file*']
741
takes_options = ['revision', 'diff-options']
742
aliases = ['di', 'dif']
745
def run(self, revision=None, file_list=None, diff_options=None):
746
from bzrlib.diff import show_diff
748
b, file_list = inner_branch_files(file_list)
750
except FileInWrongBranch:
751
if len(file_list) != 2:
752
raise BzrCommandError("Files are in different branches")
754
b, file1 = Branch.open_containing(file_list[0])
755
b2, file2 = Branch.open_containing(file_list[1])
756
if file1 != "" or file2 != "":
757
raise BzrCommandError("Files are in different branches")
759
if revision is not None:
761
raise BzrCommandError("Can't specify -r with two branches")
762
if len(revision) == 1:
763
return show_diff(b, revision[0], specific_files=file_list,
764
external_diff_options=diff_options)
765
elif len(revision) == 2:
766
return show_diff(b, revision[0], specific_files=file_list,
767
external_diff_options=diff_options,
768
revision2=revision[1])
770
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
772
return show_diff(b, None, specific_files=file_list,
773
external_diff_options=diff_options, b2=b2)
776
class cmd_deleted(Command):
777
"""List files deleted in the working tree.
779
# TODO: Show files deleted since a previous revision, or
780
# between two revisions.
781
# TODO: Much more efficient way to do this: read in new
782
# directories with readdir, rather than stating each one. Same
783
# level of effort but possibly much less IO. (Or possibly not,
784
# if the directories are very large...)
786
def run(self, show_ids=False):
787
b = Branch.open_containing('.')[0]
789
new = b.working_tree()
790
for path, ie in old.inventory.iter_entries():
791
if not new.has_id(ie.file_id):
793
print '%-50s %s' % (path, ie.file_id)
798
class cmd_modified(Command):
799
"""List files modified in working tree."""
803
from bzrlib.delta import compare_trees
805
b = Branch.open_containing('.')[0]
806
td = compare_trees(b.basis_tree(), b.working_tree())
808
for path, id, kind, text_modified, meta_modified in td.modified:
813
class cmd_added(Command):
814
"""List files added in working tree."""
818
b = Branch.open_containing('.')[0]
819
wt = b.working_tree()
820
basis_inv = b.basis_tree().inventory
823
if file_id in basis_inv:
825
path = inv.id2path(file_id)
826
if not os.access(b.abspath(path), os.F_OK):
832
class cmd_root(Command):
833
"""Show the tree root directory.
835
The root is the nearest enclosing directory with a .bzr control
837
takes_args = ['filename?']
839
def run(self, filename=None):
840
"""Print the branch root."""
841
b = Branch.open_containing(filename)[0]
845
class cmd_log(Command):
846
"""Show log of this branch.
848
To request a range of logs, you can use the command -r begin..end
849
-r revision requests a specific revision, -r ..end or -r begin.. are
853
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
855
takes_args = ['filename?']
856
takes_options = [Option('forward',
857
help='show from oldest to newest'),
858
'timezone', 'verbose',
859
'show-ids', 'revision',
860
Option('line', help='format with one line per revision'),
863
help='show revisions whose message matches this regexp',
865
Option('short', help='use moderately short format'),
868
def run(self, filename=None, timezone='original',
877
from bzrlib.log import log_formatter, show_log
879
assert message is None or isinstance(message, basestring), \
880
"invalid message argument %r" % message
881
direction = (forward and 'forward') or 'reverse'
884
b, fp = Branch.open_containing(filename)
887
inv = b.working_tree().read_working_inventory()
888
except NoWorkingTree:
889
inv = b.get_inventory(b.last_revision())
890
file_id = inv.path2id(fp)
892
file_id = None # points to branch root
894
b, relpath = Branch.open_containing('.')
900
elif len(revision) == 1:
901
rev1 = rev2 = revision[0].in_history(b).revno
902
elif len(revision) == 2:
903
rev1 = revision[0].in_history(b).revno
904
rev2 = revision[1].in_history(b).revno
906
raise BzrCommandError('bzr log --revision takes one or two values.')
908
# By this point, the revision numbers are converted to the +ve
909
# form if they were supplied in the -ve form, so we can do
910
# this comparison in relative safety
912
(rev2, rev1) = (rev1, rev2)
914
mutter('encoding log as %r', bzrlib.user_encoding)
916
# use 'replace' so that we don't abort if trying to write out
917
# in e.g. the default C locale.
918
outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
925
lf = log_formatter(log_format,
928
show_timezone=timezone)
941
class cmd_touching_revisions(Command):
942
"""Return revision-ids which affected a particular file.
944
A more user-friendly interface is "bzr log FILE"."""
946
takes_args = ["filename"]
948
def run(self, filename):
949
b, relpath = Branch.open_containing(filename)[0]
950
inv = b.working_tree().read_working_inventory()
951
file_id = inv.path2id(relpath)
952
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
953
print "%6d %s" % (revno, what)
956
class cmd_ls(Command):
957
"""List files in a tree.
959
# TODO: Take a revision or remote path and list that tree instead.
961
takes_options = ['verbose', 'revision',
962
Option('non-recursive',
963
help='don\'t recurse into sub-directories'),
965
help='Print all paths from the root of the branch.'),
966
Option('unknown', help='Print unknown files'),
967
Option('versioned', help='Print versioned files'),
968
Option('ignored', help='Print ignored files'),
970
Option('null', help='Null separate the files'),
973
def run(self, revision=None, verbose=False,
974
non_recursive=False, from_root=False,
975
unknown=False, versioned=False, ignored=False,
979
raise BzrCommandError('Cannot set both --verbose and --null')
980
all = not (unknown or versioned or ignored)
982
selection = {'I':ignored, '?':unknown, 'V':versioned}
984
b, relpath = Branch.open_containing('.')
990
tree = b.working_tree()
992
tree = b.revision_tree(revision[0].in_history(b).rev_id)
993
for fp, fc, kind, fid, entry in tree.list_files():
994
if fp.startswith(relpath):
995
fp = fp[len(relpath):]
996
if non_recursive and '/' in fp:
998
if not all and not selection[fc]:
1001
kindch = entry.kind_character()
1002
print '%-8s %s%s' % (fc, fp, kindch)
1004
sys.stdout.write(fp)
1005
sys.stdout.write('\0')
1012
class cmd_unknowns(Command):
1013
"""List unknown files."""
1016
from bzrlib.osutils import quotefn
1017
for f in Branch.open_containing('.')[0].unknowns():
1022
class cmd_ignore(Command):
1023
"""Ignore a command or pattern.
1025
To remove patterns from the ignore list, edit the .bzrignore file.
1027
If the pattern contains a slash, it is compared to the whole path
1028
from the branch root. Otherwise, it is compared to only the last
1029
component of the path. To match a file only in the root directory,
1032
Ignore patterns are case-insensitive on case-insensitive systems.
1034
Note: wildcards must be quoted from the shell on Unix.
1037
bzr ignore ./Makefile
1038
bzr ignore '*.class'
1040
# TODO: Complain if the filename is absolute
1041
takes_args = ['name_pattern']
1043
def run(self, name_pattern):
1044
from bzrlib.atomicfile import AtomicFile
1047
b, relpath = Branch.open_containing('.')
1048
ifn = b.abspath('.bzrignore')
1050
if os.path.exists(ifn):
1053
igns = f.read().decode('utf-8')
1059
# TODO: If the file already uses crlf-style termination, maybe
1060
# we should use that for the newly added lines?
1062
if igns and igns[-1] != '\n':
1064
igns += name_pattern + '\n'
1067
f = AtomicFile(ifn, 'wt')
1068
f.write(igns.encode('utf-8'))
1073
inv = b.working_tree().inventory
1074
if inv.path2id('.bzrignore'):
1075
mutter('.bzrignore is already versioned')
1077
mutter('need to make new .bzrignore file versioned')
1078
b.add(['.bzrignore'])
1082
class cmd_ignored(Command):
1083
"""List ignored files and the patterns that matched them.
1085
See also: bzr ignore"""
1088
tree = Branch.open_containing('.')[0].working_tree()
1089
for path, file_class, kind, file_id, entry in tree.list_files():
1090
if file_class != 'I':
1092
## XXX: Slightly inefficient since this was already calculated
1093
pat = tree.is_ignored(path)
1094
print '%-50s %s' % (path, pat)
1097
class cmd_lookup_revision(Command):
1098
"""Lookup the revision-id from a revision-number
1101
bzr lookup-revision 33
1104
takes_args = ['revno']
1107
def run(self, revno):
1111
raise BzrCommandError("not a valid revision-number: %r" % revno)
1113
print Branch.open_containing('.')[0].get_rev_id(revno)
1116
class cmd_export(Command):
1117
"""Export past revision to destination directory.
1119
If no revision is specified this exports the last committed revision.
1121
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1122
given, try to find the format with the extension. If no extension
1123
is found exports to a directory (equivalent to --format=dir).
1125
Root may be the top directory for tar, tgz and tbz2 formats. If none
1126
is given, the top directory will be the root name of the file."""
1127
# TODO: list known exporters
1128
takes_args = ['dest']
1129
takes_options = ['revision', 'format', 'root']
1130
def run(self, dest, revision=None, format=None, root=None):
1132
b = Branch.open_containing('.')[0]
1133
if revision is None:
1134
rev_id = b.last_revision()
1136
if len(revision) != 1:
1137
raise BzrError('bzr export --revision takes exactly 1 argument')
1138
rev_id = revision[0].in_history(b).rev_id
1139
t = b.revision_tree(rev_id)
1140
arg_root, ext = os.path.splitext(os.path.basename(dest))
1141
if ext in ('.gz', '.bz2'):
1142
new_root, new_ext = os.path.splitext(arg_root)
1143
if new_ext == '.tar':
1149
if ext in (".tar",):
1151
elif ext in (".tar.gz", ".tgz"):
1153
elif ext in (".tar.bz2", ".tbz2"):
1157
t.export(dest, format, root)
1160
class cmd_cat(Command):
1161
"""Write a file's text from a previous revision."""
1163
takes_options = ['revision']
1164
takes_args = ['filename']
1167
def run(self, filename, revision=None):
1168
if revision is None:
1169
raise BzrCommandError("bzr cat requires a revision number")
1170
elif len(revision) != 1:
1171
raise BzrCommandError("bzr cat --revision takes exactly one number")
1172
b, relpath = Branch.open_containing(filename)
1173
b.print_file(relpath, revision[0].in_history(b).revno)
1176
class cmd_local_time_offset(Command):
1177
"""Show the offset in seconds from GMT to local time."""
1181
print bzrlib.osutils.local_time_offset()
1185
class cmd_commit(Command):
1186
"""Commit changes into a new revision.
1188
If no arguments are given, the entire tree is committed.
1190
If selected files are specified, only changes to those files are
1191
committed. If a directory is specified then the directory and everything
1192
within it is committed.
1194
A selected-file commit may fail in some cases where the committed
1195
tree would be invalid, such as trying to commit a file in a
1196
newly-added directory that is not itself committed.
1198
# TODO: Run hooks on tree to-be-committed, and after commit.
1200
# TODO: Strict commit that fails if there are deleted files.
1201
# (what does "deleted files" mean ??)
1203
# TODO: Give better message for -s, --summary, used by tla people
1205
# XXX: verbose currently does nothing
1207
takes_args = ['selected*']
1208
takes_options = ['message', 'verbose',
1210
help='commit even if nothing has changed'),
1211
Option('file', type=str,
1213
help='file containing commit message'),
1215
help="refuse to commit if there are unknown "
1216
"files in the working tree."),
1218
aliases = ['ci', 'checkin']
1220
def run(self, message=None, file=None, verbose=True, selected_list=None,
1221
unchanged=False, strict=False):
1222
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1224
from bzrlib.msgeditor import edit_commit_message
1225
from bzrlib.status import show_status
1226
from cStringIO import StringIO
1228
b, selected_list = branch_files(selected_list)
1229
if message is None and not file:
1230
catcher = StringIO()
1231
show_status(b, specific_files=selected_list,
1233
message = edit_commit_message(catcher.getvalue())
1236
raise BzrCommandError("please specify a commit message"
1237
" with either --message or --file")
1238
elif message and file:
1239
raise BzrCommandError("please specify either --message or --file")
1243
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1246
raise BzrCommandError("empty commit message specified")
1249
b.working_tree().commit(message, specific_files=selected_list,
1250
allow_pointless=unchanged, strict=strict)
1251
except PointlessCommit:
1252
# FIXME: This should really happen before the file is read in;
1253
# perhaps prepare the commit; get the message; then actually commit
1254
raise BzrCommandError("no changes to commit",
1255
["use --unchanged to commit anyhow"])
1256
except ConflictsInTree:
1257
raise BzrCommandError("Conflicts detected in working tree. "
1258
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1259
except StrictCommitFailed:
1260
raise BzrCommandError("Commit refused because there are unknown "
1261
"files in the working tree.")
1264
class cmd_check(Command):
1265
"""Validate consistency of branch history.
1267
This command checks various invariants about the branch storage to
1268
detect data corruption or bzr bugs.
1270
takes_args = ['dir?']
1271
takes_options = ['verbose']
1273
def run(self, dir='.', verbose=False):
1274
from bzrlib.check import check
1275
check(Branch.open_containing(dir)[0], verbose)
1278
class cmd_scan_cache(Command):
1281
from bzrlib.hashcache import HashCache
1287
print '%6d stats' % c.stat_count
1288
print '%6d in hashcache' % len(c._cache)
1289
print '%6d files removed from cache' % c.removed_count
1290
print '%6d hashes updated' % c.update_count
1291
print '%6d files changed too recently to cache' % c.danger_count
1298
class cmd_upgrade(Command):
1299
"""Upgrade branch storage to current format.
1301
The check command or bzr developers may sometimes advise you to run
1304
This version of this command upgrades from the full-text storage
1305
used by bzr 0.0.8 and earlier to the weave format (v5).
1307
takes_args = ['dir?']
1309
def run(self, dir='.'):
1310
from bzrlib.upgrade import upgrade
1314
class cmd_whoami(Command):
1315
"""Show bzr user id."""
1316
takes_options = ['email']
1319
def run(self, email=False):
1321
b = bzrlib.branch.Branch.open_containing('.')[0]
1322
config = bzrlib.config.BranchConfig(b)
1323
except NotBranchError:
1324
config = bzrlib.config.GlobalConfig()
1327
print config.user_email()
1329
print config.username()
1331
class cmd_nick(Command):
1333
Print or set the branch nickname.
1334
If unset, the tree root directory name is used as the nickname
1335
To print the current nickname, execute with no argument.
1337
takes_args = ['nickname?']
1338
def run(self, nickname=None):
1339
branch = Branch.open_containing('.')[0]
1340
if nickname is None:
1341
self.printme(branch)
1343
branch.nick = nickname
1346
def printme(self, branch):
1349
class cmd_selftest(Command):
1350
"""Run internal test suite.
1352
This creates temporary test directories in the working directory,
1353
but not existing data is affected. These directories are deleted
1354
if the tests pass, or left behind to help in debugging if they
1355
fail and --keep-output is specified.
1357
If arguments are given, they are regular expressions that say
1358
which tests should run.
1360
# TODO: --list should give a list of all available tests
1362
takes_args = ['testspecs*']
1363
takes_options = ['verbose',
1364
Option('one', help='stop when one test fails'),
1365
Option('keep-output',
1366
help='keep output directories when tests fail')
1369
def run(self, testspecs_list=None, verbose=False, one=False,
1372
from bzrlib.selftest import selftest
1373
# we don't want progress meters from the tests to go to the
1374
# real output; and we don't want log messages cluttering up
1376
save_ui = bzrlib.ui.ui_factory
1377
bzrlib.trace.info('running tests...')
1379
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1380
if testspecs_list is not None:
1381
pattern = '|'.join(testspecs_list)
1384
result = selftest(verbose=verbose,
1386
stop_on_failure=one,
1387
keep_output=keep_output)
1389
bzrlib.trace.info('tests passed')
1391
bzrlib.trace.info('tests failed')
1392
return int(not result)
1394
bzrlib.ui.ui_factory = save_ui
1398
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1399
# is bzrlib itself in a branch?
1400
bzrrev = bzrlib.get_bzr_revision()
1402
print " (bzr checkout, revision %d {%s})" % bzrrev
1403
print bzrlib.__copyright__
1404
print "http://bazaar-ng.org/"
1406
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1407
print "you may use, modify and redistribute it under the terms of the GNU"
1408
print "General Public License version 2 or later."
1411
class cmd_version(Command):
1412
"""Show version of bzr."""
1417
class cmd_rocks(Command):
1418
"""Statement of optimism."""
1422
print "it sure does!"
1425
class cmd_find_merge_base(Command):
1426
"""Find and print a base revision for merging two branches.
1428
# TODO: Options to specify revisions on either side, as if
1429
# merging only part of the history.
1430
takes_args = ['branch', 'other']
1434
def run(self, branch, other):
1435
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1437
branch1 = Branch.open_containing(branch)[0]
1438
branch2 = Branch.open_containing(other)[0]
1440
history_1 = branch1.revision_history()
1441
history_2 = branch2.revision_history()
1443
last1 = branch1.last_revision()
1444
last2 = branch2.last_revision()
1446
source = MultipleRevisionSources(branch1, branch2)
1448
base_rev_id = common_ancestor(last1, last2, source)
1450
print 'merge base is revision %s' % base_rev_id
1454
if base_revno is None:
1455
raise bzrlib.errors.UnrelatedBranches()
1457
print ' r%-6d in %s' % (base_revno, branch)
1459
other_revno = branch2.revision_id_to_revno(base_revid)
1461
print ' r%-6d in %s' % (other_revno, other)
1465
class cmd_merge(Command):
1466
"""Perform a three-way merge.
1468
The branch is the branch you will merge from. By default, it will
1469
merge the latest revision. If you specify a revision, that
1470
revision will be merged. If you specify two revisions, the first
1471
will be used as a BASE, and the second one as OTHER. Revision
1472
numbers are always relative to the specified branch.
1474
By default bzr will try to merge in all new work from the other
1475
branch, automatically determining an appropriate base. If this
1476
fails, you may need to give an explicit base.
1480
To merge the latest revision from bzr.dev
1481
bzr merge ../bzr.dev
1483
To merge changes up to and including revision 82 from bzr.dev
1484
bzr merge -r 82 ../bzr.dev
1486
To merge the changes introduced by 82, without previous changes:
1487
bzr merge -r 81..82 ../bzr.dev
1489
merge refuses to run if there are any uncommitted changes, unless
1492
takes_args = ['branch?']
1493
takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1494
Option('show-base', help="Show base revision text in "
1497
def run(self, branch=None, revision=None, force=False, merge_type=None,
1498
show_base=False, reprocess=False):
1499
from bzrlib.merge import merge
1500
from bzrlib.merge_core import ApplyMerge3
1501
if merge_type is None:
1502
merge_type = ApplyMerge3
1504
branch = Branch.open_containing('.')[0].get_parent()
1506
raise BzrCommandError("No merge location known or specified.")
1508
print "Using saved location: %s" % branch
1509
if revision is None or len(revision) < 1:
1511
other = [branch, -1]
1513
if len(revision) == 1:
1515
other_branch = Branch.open_containing(branch)[0]
1516
revno = revision[0].in_history(other_branch).revno
1517
other = [branch, revno]
1519
assert len(revision) == 2
1520
if None in revision:
1521
raise BzrCommandError(
1522
"Merge doesn't permit that revision specifier.")
1523
b = Branch.open_containing(branch)[0]
1525
base = [branch, revision[0].in_history(b).revno]
1526
other = [branch, revision[1].in_history(b).revno]
1529
conflict_count = merge(other, base, check_clean=(not force),
1530
merge_type=merge_type, reprocess=reprocess,
1531
show_base=show_base)
1532
if conflict_count != 0:
1536
except bzrlib.errors.AmbiguousBase, e:
1537
m = ("sorry, bzr can't determine the right merge base yet\n"
1538
"candidates are:\n "
1539
+ "\n ".join(e.bases)
1541
"please specify an explicit base with -r,\n"
1542
"and (if you want) report this to the bzr developers\n")
1546
class cmd_remerge(Command):
1549
takes_args = ['file*']
1550
takes_options = ['merge-type', 'reprocess',
1551
Option('show-base', help="Show base revision text in "
1554
def run(self, file_list=None, merge_type=None, show_base=False,
1556
from bzrlib.merge import merge_inner, transform_tree
1557
from bzrlib.merge_core import ApplyMerge3
1558
if merge_type is None:
1559
merge_type = ApplyMerge3
1560
b, file_list = branch_files(file_list)
1563
pending_merges = b.working_tree().pending_merges()
1564
if len(pending_merges) != 1:
1565
raise BzrCommandError("Sorry, remerge only works after normal"
1566
+ " merges. Not cherrypicking or"
1568
this_tree = b.working_tree()
1569
base_revision = common_ancestor(b.last_revision(),
1570
pending_merges[0], b)
1571
base_tree = b.revision_tree(base_revision)
1572
other_tree = b.revision_tree(pending_merges[0])
1573
interesting_ids = None
1574
if file_list is not None:
1575
interesting_ids = set()
1576
for filename in file_list:
1577
file_id = this_tree.path2id(filename)
1578
interesting_ids.add(file_id)
1579
if this_tree.kind(file_id) != "directory":
1582
for name, ie in this_tree.inventory.iter_entries(file_id):
1583
interesting_ids.add(ie.file_id)
1584
transform_tree(this_tree, b.basis_tree(), interesting_ids)
1585
if file_list is None:
1586
restore_files = list(this_tree.iter_conflicts())
1588
restore_files = file_list
1589
for filename in restore_files:
1591
restore(this_tree.abspath(filename))
1592
except NotConflicted:
1594
conflicts = merge_inner(b, other_tree, base_tree,
1595
interesting_ids = interesting_ids,
1596
other_rev_id=pending_merges[0],
1597
merge_type=merge_type,
1598
show_base=show_base,
1599
reprocess=reprocess)
1607
class cmd_revert(Command):
1608
"""Reverse all changes since the last commit.
1610
Only versioned files are affected. Specify filenames to revert only
1611
those files. By default, any files that are changed will be backed up
1612
first. Backup files have a '~' appended to their name.
1614
takes_options = ['revision', 'no-backup']
1615
takes_args = ['file*']
1616
aliases = ['merge-revert']
1618
def run(self, revision=None, no_backup=False, file_list=None):
1619
from bzrlib.merge import merge_inner
1620
from bzrlib.commands import parse_spec
1621
if file_list is not None:
1622
if len(file_list) == 0:
1623
raise BzrCommandError("No files specified")
1626
if revision is None:
1628
b = Branch.open_containing('.')[0]
1629
rev_id = b.last_revision()
1630
elif len(revision) != 1:
1631
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1633
b, file_list = branch_files(file_list)
1634
rev_id = revision[0].in_history(b).rev_id
1635
b.working_tree().revert(file_list, b.revision_tree(rev_id),
1639
class cmd_assert_fail(Command):
1640
"""Test reporting of assertion failures"""
1643
assert False, "always fails"
1646
class cmd_help(Command):
1647
"""Show help on a command or other topic.
1649
For a list of all available commands, say 'bzr help commands'."""
1650
takes_options = ['long']
1651
takes_args = ['topic?']
1655
def run(self, topic=None, long=False):
1657
if topic is None and long:
1662
class cmd_shell_complete(Command):
1663
"""Show appropriate completions for context.
1665
For a list of all available commands, say 'bzr shell-complete'."""
1666
takes_args = ['context?']
1671
def run(self, context=None):
1672
import shellcomplete
1673
shellcomplete.shellcomplete(context)
1676
class cmd_fetch(Command):
1677
"""Copy in history from another branch but don't merge it.
1679
This is an internal method used for pull and merge."""
1681
takes_args = ['from_branch', 'to_branch']
1682
def run(self, from_branch, to_branch):
1683
from bzrlib.fetch import Fetcher
1684
from bzrlib.branch import Branch
1685
from_b = Branch.open(from_branch)
1686
to_b = Branch.open(to_branch)
1691
Fetcher(to_b, from_b)
1698
class cmd_missing(Command):
1699
"""What is missing in this branch relative to other branch.
1701
# TODO: rewrite this in terms of ancestry so that it shows only
1704
takes_args = ['remote?']
1705
aliases = ['mis', 'miss']
1706
# We don't have to add quiet to the list, because
1707
# unknown options are parsed as booleans
1708
takes_options = ['verbose', 'quiet']
1711
def run(self, remote=None, verbose=False, quiet=False):
1712
from bzrlib.errors import BzrCommandError
1713
from bzrlib.missing import show_missing
1715
if verbose and quiet:
1716
raise BzrCommandError('Cannot pass both quiet and verbose')
1718
b = Branch.open_containing('.')[0]
1719
parent = b.get_parent()
1722
raise BzrCommandError("No missing location known or specified.")
1725
print "Using last location: %s" % parent
1727
elif parent is None:
1728
# We only update parent if it did not exist, missing
1729
# should not change the parent
1730
b.set_parent(remote)
1731
br_remote = Branch.open_containing(remote)[0]
1732
return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1735
class cmd_plugins(Command):
1740
import bzrlib.plugin
1741
from inspect import getdoc
1742
for plugin in bzrlib.plugin.all_plugins:
1743
if hasattr(plugin, '__path__'):
1744
print plugin.__path__[0]
1745
elif hasattr(plugin, '__file__'):
1746
print plugin.__file__
1752
print '\t', d.split('\n')[0]
1755
class cmd_testament(Command):
1756
"""Show testament (signing-form) of a revision."""
1757
takes_options = ['revision', 'long']
1758
takes_args = ['branch?']
1760
def run(self, branch='.', revision=None, long=False):
1761
from bzrlib.testament import Testament
1762
b = Branch.open_containing(branch)[0]
1765
if revision is None:
1766
rev_id = b.last_revision()
1768
rev_id = revision[0].in_history(b).rev_id
1769
t = Testament.from_revision(b, rev_id)
1771
sys.stdout.writelines(t.as_text_lines())
1773
sys.stdout.write(t.as_short_text())
1778
class cmd_annotate(Command):
1779
"""Show the origin of each line in a file.
1781
This prints out the given file with an annotation on the left side
1782
indicating which revision, author and date introduced the change.
1784
If the origin is the same for a run of consecutive lines, it is
1785
shown only at the top, unless the --all option is given.
1787
# TODO: annotate directories; showing when each file was last changed
1788
# TODO: annotate a previous version of a file
1789
# TODO: if the working copy is modified, show annotations on that
1790
# with new uncommitted lines marked
1791
aliases = ['blame', 'praise']
1792
takes_args = ['filename']
1793
takes_options = [Option('all', help='show annotations on all lines'),
1794
Option('long', help='show date in annotations'),
1798
def run(self, filename, all=False, long=False):
1799
from bzrlib.annotate import annotate_file
1800
b, relpath = Branch.open_containing(filename)
1803
tree = WorkingTree(b.base, b)
1804
tree = b.revision_tree(b.last_revision())
1805
file_id = tree.inventory.path2id(relpath)
1806
file_version = tree.inventory[file_id].revision
1807
annotate_file(b, file_version, file_id, long, all, sys.stdout)
1812
class cmd_re_sign(Command):
1813
"""Create a digital signature for an existing revision."""
1814
# TODO be able to replace existing ones.
1816
hidden = True # is this right ?
1817
takes_args = ['revision_id?']
1818
takes_options = ['revision']
1820
def run(self, revision_id=None, revision=None):
1821
import bzrlib.config as config
1822
import bzrlib.gpg as gpg
1823
if revision_id is not None and revision is not None:
1824
raise BzrCommandError('You can only supply one of revision_id or --revision')
1825
if revision_id is None and revision is None:
1826
raise BzrCommandError('You must supply either --revision or a revision_id')
1827
b = Branch.open_containing('.')[0]
1828
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1829
if revision_id is not None:
1830
b.sign_revision(revision_id, gpg_strategy)
1831
elif revision is not None:
1832
if len(revision) == 1:
1833
revno, rev_id = revision[0].in_history(b)
1834
b.sign_revision(rev_id, gpg_strategy)
1835
elif len(revision) == 2:
1836
# are they both on rh- if so we can walk between them
1837
# might be nice to have a range helper for arbitrary
1838
# revision paths. hmm.
1839
from_revno, from_revid = revision[0].in_history(b)
1840
to_revno, to_revid = revision[1].in_history(b)
1841
if to_revid is None:
1842
to_revno = b.revno()
1843
if from_revno is None or to_revno is None:
1844
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1845
for revno in range(from_revno, to_revno + 1):
1846
b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1848
raise BzrCommandError('Please supply either one revision, or a range.')
1851
# these get imported and then picked up by the scan for cmd_*
1852
# TODO: Some more consistent way to split command definitions across files;
1853
# we do need to load at least some information about them to know of
1855
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore