1
# Copyright (C) 2004, 2005, 2006 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
"""builtin bzr commands"""
23
from shutil import rmtree
28
from bzrlib.branch import Branch
29
import bzrlib.bzrdir as bzrdir
30
from bzrlib.commands import Command, display_command
31
from bzrlib.revision import common_ancestor
32
import bzrlib.errors as errors
33
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError,
34
NotBranchError, DivergedBranches, NotConflicted,
35
NoSuchFile, NoWorkingTree, FileInWrongBranch,
37
from bzrlib.log import show_one_log
38
from bzrlib.merge import Merge3Merger
39
from bzrlib.option import Option
41
from bzrlib.progress import DummyProgress, ProgressPhase
42
from bzrlib.revisionspec import RevisionSpec
44
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
45
from bzrlib.transport.local import LocalTransport
47
import bzrlib.urlutils as urlutils
48
from bzrlib.workingtree import WorkingTree
51
def tree_files(file_list, default_branch=u'.'):
53
return internal_tree_files(file_list, default_branch)
54
except FileInWrongBranch, e:
55
raise BzrCommandError("%s is not in the same branch as %s" %
56
(e.path, file_list[0]))
59
# XXX: Bad function name; should possibly also be a class method of
60
# WorkingTree rather than a function.
61
def internal_tree_files(file_list, default_branch=u'.'):
62
"""Convert command-line paths to a WorkingTree and relative paths.
64
This is typically used for command-line processors that take one or
65
more filenames, and infer the workingtree that contains them.
67
The filenames given are not required to exist.
69
:param file_list: Filenames to convert.
71
:param default_branch: Fallback tree path to use if file_list is empty or None.
73
:return: workingtree, [relative_paths]
75
if file_list is None or len(file_list) == 0:
76
return WorkingTree.open_containing(default_branch)[0], file_list
77
tree = WorkingTree.open_containing(file_list[0])[0]
79
for filename in file_list:
81
new_list.append(tree.relpath(filename))
82
except errors.PathNotChild:
83
raise FileInWrongBranch(tree.branch, filename)
87
def get_format_type(typestring):
88
"""Parse and return a format specifier."""
89
if typestring == "weave":
90
return bzrdir.BzrDirFormat6()
91
if typestring == "default":
92
return bzrdir.BzrDirMetaFormat1()
93
if typestring == "metaweave":
94
format = bzrdir.BzrDirMetaFormat1()
95
format.repository_format = bzrlib.repository.RepositoryFormat7()
97
if typestring == "knit":
98
format = bzrdir.BzrDirMetaFormat1()
99
format.repository_format = bzrlib.repository.RepositoryFormatKnit1()
101
msg = "Unknown bzr format %s. Current formats are: default, knit,\n" \
102
"metaweave and weave" % typestring
103
raise BzrCommandError(msg)
106
# TODO: Make sure no commands unconditionally use the working directory as a
107
# branch. If a filename argument is used, the first of them should be used to
108
# specify the branch. (Perhaps this can be factored out into some kind of
109
# Argument class, representing a file in a branch, where the first occurrence
112
class cmd_status(Command):
113
"""Display status summary.
115
This reports on versioned and unknown files, reporting them
116
grouped by state. Possible states are:
119
Versioned in the working copy but not in the previous revision.
122
Versioned in the previous revision but removed or deleted
126
Path of this file changed from the previous revision;
127
the text may also have changed. This includes files whose
128
parent directory was renamed.
131
Text has changed since the previous revision.
134
Nothing about this file has changed since the previous revision.
135
Only shown with --all.
138
Not versioned and not matching an ignore pattern.
140
To see ignored files use 'bzr ignored'. For details in the
141
changes to file texts, use 'bzr diff'.
143
If no arguments are specified, the status of the entire working
144
directory is shown. Otherwise, only the status of the specified
145
files or directories is reported. If a directory is given, status
146
is reported for everything inside that directory.
148
If a revision argument is given, the status is calculated against
149
that revision, or between two revisions if two are provided.
152
# TODO: --no-recurse, --recurse options
154
takes_args = ['file*']
155
takes_options = ['all', 'show-ids', 'revision']
156
aliases = ['st', 'stat']
158
encoding_type = 'replace'
161
def run(self, all=False, show_ids=False, file_list=None, revision=None):
162
from bzrlib.status import show_tree_status
164
tree, file_list = tree_files(file_list)
166
show_tree_status(tree, show_unchanged=all, show_ids=show_ids,
167
specific_files=file_list, revision=revision,
171
class cmd_cat_revision(Command):
172
"""Write out metadata for a revision.
174
The revision to print can either be specified by a specific
175
revision identifier, or you can use --revision.
179
takes_args = ['revision_id?']
180
takes_options = ['revision']
183
def run(self, revision_id=None, revision=None):
185
if revision_id is not None and revision is not None:
186
raise BzrCommandError('You can only supply one of revision_id or --revision')
187
if revision_id is None and revision is None:
188
raise BzrCommandError('You must supply either --revision or a revision_id')
189
b = WorkingTree.open_containing(u'.')[0].branch
191
# TODO: jam 20060112 should cat-revision always output utf-8?
192
if revision_id is not None:
193
self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
194
elif revision is not None:
197
raise BzrCommandError('You cannot specify a NULL revision.')
198
revno, rev_id = rev.in_history(b)
199
self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
202
class cmd_revno(Command):
203
"""Show current revision number.
205
This is equal to the number of revisions on this branch.
208
takes_args = ['location?']
211
def run(self, location=u'.'):
212
self.outf.write(str(Branch.open_containing(location)[0].revno()))
213
self.outf.write('\n')
216
class cmd_revision_info(Command):
217
"""Show revision number and revision id for a given revision identifier.
220
takes_args = ['revision_info*']
221
takes_options = ['revision']
224
def run(self, revision=None, revision_info_list=[]):
227
if revision is not None:
228
revs.extend(revision)
229
if revision_info_list is not None:
230
for rev in revision_info_list:
231
revs.append(RevisionSpec(rev))
233
raise BzrCommandError('You must supply a revision identifier')
235
b = WorkingTree.open_containing(u'.')[0].branch
238
revinfo = rev.in_history(b)
239
if revinfo.revno is None:
240
print ' %s' % revinfo.rev_id
242
print '%4d %s' % (revinfo.revno, revinfo.rev_id)
245
class cmd_add(Command):
246
"""Add specified files or directories.
248
In non-recursive mode, all the named items are added, regardless
249
of whether they were previously ignored. A warning is given if
250
any of the named files are already versioned.
252
In recursive mode (the default), files are treated the same way
253
but the behaviour for directories is different. Directories that
254
are already versioned do not give a warning. All directories,
255
whether already versioned or not, are searched for files or
256
subdirectories that are neither versioned or ignored, and these
257
are added. This search proceeds recursively into versioned
258
directories. If no names are given '.' is assumed.
260
Therefore simply saying 'bzr add' will version all files that
261
are currently unknown.
263
Adding a file whose parent directory is not versioned will
264
implicitly add the parent, and so on up to the root. This means
265
you should never need to explictly add a directory, they'll just
266
get added when you add a file in the directory.
268
--dry-run will show which files would be added, but not actually
271
takes_args = ['file*']
272
takes_options = ['no-recurse', 'dry-run', 'verbose']
273
encoding_type = 'replace'
275
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
278
action = bzrlib.add.AddAction(to_file=self.outf,
279
should_add=(not dry_run), should_print=(not is_quiet()))
281
added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
284
for glob in sorted(ignored.keys()):
285
match_len = len(ignored[glob])
287
for path in ignored[glob]:
288
self.outf.write("ignored %s matching \"%s\"\n"
291
self.outf.write("ignored %d file(s) matching \"%s\"\n"
293
self.outf.write("If you wish to add some of these files,"
294
" please add them by name.\n")
297
class cmd_mkdir(Command):
298
"""Create a new versioned directory.
300
This is equivalent to creating the directory and then adding it.
302
takes_args = ['dir+']
303
encoding_type = 'replace'
305
def run(self, dir_list):
308
wt, dd = WorkingTree.open_containing(d)
310
print >>self.outf, 'added', d
313
class cmd_relpath(Command):
314
"""Show path of a file relative to root"""
315
takes_args = ['filename']
319
def run(self, filename):
320
# TODO: jam 20050106 Can relpath return a munged path if
321
# sys.stdout encoding cannot represent it?
322
tree, relpath = WorkingTree.open_containing(filename)
323
self.outf.write(relpath)
324
self.outf.write('\n')
327
class cmd_inventory(Command):
328
"""Show inventory of the current working copy or a revision.
330
It is possible to limit the output to a particular entry
331
type using the --kind option. For example; --kind file.
333
takes_options = ['revision', 'show-ids', 'kind']
336
def run(self, revision=None, show_ids=False, kind=None):
337
if kind and kind not in ['file', 'directory', 'symlink']:
338
raise BzrCommandError('invalid kind specified')
339
tree = WorkingTree.open_containing(u'.')[0]
341
inv = tree.read_working_inventory()
343
if len(revision) > 1:
344
raise BzrCommandError('bzr inventory --revision takes'
345
' exactly one revision identifier')
346
inv = tree.branch.repository.get_revision_inventory(
347
revision[0].in_history(tree.branch).rev_id)
349
for path, entry in inv.entries():
350
if kind and kind != entry.kind:
353
self.outf.write('%-50s %s\n' % (path, entry.file_id))
355
self.outf.write(path)
356
self.outf.write('\n')
359
class cmd_mv(Command):
360
"""Move or rename a file.
363
bzr mv OLDNAME NEWNAME
364
bzr mv SOURCE... DESTINATION
366
If the last argument is a versioned directory, all the other names
367
are moved into it. Otherwise, there must be exactly two arguments
368
and the file is changed to a new name, which must not already exist.
370
Files cannot be moved between branches.
372
takes_args = ['names*']
373
aliases = ['move', 'rename']
375
encoding_type = 'replace'
377
def run(self, names_list):
378
if len(names_list) < 2:
379
raise BzrCommandError("missing file argument")
380
tree, rel_names = tree_files(names_list)
382
if os.path.isdir(names_list[-1]):
383
# move into existing directory
384
for pair in tree.move(rel_names[:-1], rel_names[-1]):
385
self.outf.write("%s => %s\n" % pair)
387
if len(names_list) != 2:
388
raise BzrCommandError('to mv multiple files the destination '
389
'must be a versioned directory')
390
tree.rename_one(rel_names[0], rel_names[1])
391
self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
394
class cmd_pull(Command):
395
"""Turn this branch into a mirror of another branch.
397
This command only works on branches that have not diverged. Branches are
398
considered diverged if the destination branch's most recent commit is one
399
that has not been merged (directly or indirectly) into the parent.
401
If branches have diverged, you can use 'bzr merge' to integrate the changes
402
from one into the other. Once one branch has merged, the other should
403
be able to pull it again.
405
If branches have diverged, you can use 'bzr merge' to pull the text changes
406
from one into the other. Once one branch has merged, the other should
407
be able to pull it again.
409
If you want to forget your local changes and just update your branch to
410
match the remote one, use pull --overwrite.
412
If there is no default location set, the first pull will set it. After
413
that, you can omit the location to use the default. To change the
414
default, use --remember.
416
takes_options = ['remember', 'overwrite', 'revision', 'verbose']
417
takes_args = ['location?']
418
encoding_type = 'replace'
420
def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
421
# FIXME: too much stuff is in the command class
423
tree_to = WorkingTree.open_containing(u'.')[0]
424
branch_to = tree_to.branch
425
except NoWorkingTree:
427
branch_to = Branch.open_containing(u'.')[0]
428
stored_loc = branch_to.get_parent()
430
if stored_loc is None:
431
raise BzrCommandError("No pull location known or specified.")
433
display_url = urlutils.unescape_for_display(stored_loc,
435
self.outf.write("Using saved location: %s\n" % display_url)
436
location = stored_loc
438
branch_from = Branch.open(location)
440
if branch_to.get_parent() is None or remember:
441
branch_to.set_parent(branch_from.base)
446
elif len(revision) == 1:
447
rev_id = revision[0].in_history(branch_from).rev_id
449
raise BzrCommandError('bzr pull --revision takes one value.')
451
old_rh = branch_to.revision_history()
452
if tree_to is not None:
453
count = tree_to.pull(branch_from, overwrite, rev_id)
455
count = branch_to.pull(branch_from, overwrite, rev_id)
456
note('%d revision(s) pulled.' % (count,))
459
new_rh = branch_to.revision_history()
462
from bzrlib.log import show_changed_revisions
463
show_changed_revisions(branch_to, old_rh, new_rh,
467
class cmd_push(Command):
468
"""Update a mirror of this branch.
470
The target branch will not have its working tree populated because this
471
is both expensive, and is not supported on remote file systems.
473
Some smart servers or protocols *may* put the working tree in place in
476
This command only works on branches that have not diverged. Branches are
477
considered diverged if the destination branch's most recent commit is one
478
that has not been merged (directly or indirectly) by the source branch.
480
If branches have diverged, you can use 'bzr push --overwrite' to replace
481
the other branch completely, discarding its unmerged changes.
483
If you want to ensure you have the different changes in the other branch,
484
do a merge (see bzr help merge) from the other branch, and commit that.
485
After that you will be able to do a push without '--overwrite'.
487
If there is no default push location set, the first push will set it.
488
After that, you can omit the location to use the default. To change the
489
default, use --remember.
491
takes_options = ['remember', 'overwrite', 'verbose',
492
Option('create-prefix',
493
help='Create the path leading up to the branch '
494
'if it does not already exist')]
495
takes_args = ['location?']
496
encoding_type = 'replace'
498
def run(self, location=None, remember=False, overwrite=False,
499
create_prefix=False, verbose=False):
500
# FIXME: Way too big! Put this into a function called from the
502
from bzrlib.transport import get_transport
504
br_from = Branch.open_containing('.')[0]
505
stored_loc = br_from.get_push_location()
507
if stored_loc is None:
508
raise BzrCommandError("No push location known or specified.")
510
self.outf.write("Using saved location: %s"
511
% urlutils.unescape_for_display(stored_loc))
512
location = stored_loc
514
transport = get_transport(location)
515
location_url = transport.base
516
if br_from.get_push_location() is None or remember:
517
br_from.set_push_location(location_url)
519
dir_to = bzrlib.bzrdir.BzrDir.open(location_url)
520
br_to = dir_to.open_branch()
521
except NotBranchError:
523
transport = transport.clone('..')
524
if not create_prefix:
526
relurl = transport.relpath(location_url)
527
mutter('creating directory %s => %s', location_url, relurl)
528
transport.mkdir(relurl)
530
raise BzrCommandError("Parent directory of %s "
531
"does not exist." % location)
533
current = transport.base
534
needed = [(transport, transport.relpath(location_url))]
537
transport, relpath = needed[-1]
538
transport.mkdir(relpath)
541
new_transport = transport.clone('..')
542
needed.append((new_transport,
543
new_transport.relpath(transport.base)))
544
if new_transport.base == transport.base:
545
raise BzrCommandError("Could not create "
547
dir_to = br_from.bzrdir.clone(location_url)
548
br_to = dir_to.open_branch()
549
old_rh = br_to.revision_history()
552
tree_to = dir_to.open_workingtree()
553
except errors.NotLocalUrl:
554
# TODO: This should be updated for branches which don't have a
555
# working tree, as opposed to ones where we just couldn't
557
warning('This transport does not update the working '
558
'tree of: %s' % (br_to.base,))
559
count = br_to.pull(br_from, overwrite)
560
except NoWorkingTree:
561
count = br_to.pull(br_from, overwrite)
563
count = tree_to.pull(br_from, overwrite)
564
except DivergedBranches:
565
raise BzrCommandError("These branches have diverged."
566
" Try a merge then push with overwrite.")
567
note('%d revision(s) pushed.' % (count,))
570
new_rh = br_to.revision_history()
573
from bzrlib.log import show_changed_revisions
574
show_changed_revisions(br_to, old_rh, new_rh,
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
--basis is to speed up branching from remote branches. When specified, it
588
copies all the file-contents, inventory and revision data from the basis
589
branch before copying anything from the remote branch.
591
takes_args = ['from_location', 'to_location?']
592
takes_options = ['revision', 'basis']
593
aliases = ['get', 'clone']
595
def run(self, from_location, to_location=None, revision=None, basis=None):
596
from bzrlib.transport import get_transport
597
from bzrlib.osutils import rmtree
600
elif len(revision) > 1:
601
raise BzrCommandError(
602
'bzr branch --revision takes exactly 1 revision value')
604
br_from = Branch.open(from_location)
606
if e.errno == errno.ENOENT:
607
raise BzrCommandError('Source location "%s" does not'
608
' exist.' % to_location)
613
if basis is not None:
614
basis_dir = bzrdir.BzrDir.open_containing(basis)[0]
617
if len(revision) == 1 and revision[0] is not None:
618
revision_id = revision[0].in_history(br_from)[1]
620
# FIXME - wt.last_revision, fallback to branch, fall back to
621
# None or perhaps NULL_REVISION to mean copy nothing
623
revision_id = br_from.last_revision()
624
if to_location is None:
625
to_location = os.path.basename(from_location.rstrip("/\\"))
628
name = os.path.basename(to_location) + '\n'
630
to_transport = get_transport(to_location)
632
to_transport.mkdir('.')
633
except bzrlib.errors.FileExists:
634
raise BzrCommandError('Target directory "%s" already'
635
' exists.' % to_location)
636
except bzrlib.errors.NoSuchFile:
637
raise BzrCommandError('Parent of "%s" does not exist.' %
640
# preserve whatever source format we have.
641
dir = br_from.bzrdir.sprout(to_transport.base,
642
revision_id, basis_dir)
643
branch = dir.open_branch()
644
except bzrlib.errors.NoSuchRevision:
645
# TODO: jam 20060426 This only works on local paths
646
# and it would be nice if 'bzr branch' could
647
# work on a remote path
649
msg = "The branch %s has no revision %s." % (from_location, revision[0])
650
raise BzrCommandError(msg)
651
except bzrlib.errors.UnlistableBranch:
653
msg = "The branch %s cannot be used as a --basis" % (basis,)
654
raise BzrCommandError(msg)
656
branch.control_files.put_utf8('branch-name', name)
657
note('Branched %d revision(s).' % branch.revno())
662
class cmd_checkout(Command):
663
"""Create a new checkout of an existing branch.
665
If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree for
666
the branch found in '.'. This is useful if you have removed the working tree
667
or if it was never created - i.e. if you pushed the branch to its current
670
If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION will
671
be used. In other words, "checkout ../foo/bar" will attempt to create ./bar.
673
To retrieve the branch as of a particular revision, supply the --revision
674
parameter, as in "checkout foo/bar -r 5". Note that this will be immediately
675
out of date [so you cannot commit] but it may be useful (i.e. to examine old
678
--basis is to speed up checking out from remote branches. When specified, it
679
uses the inventory and file contents from the basis branch in preference to the
680
branch being checked out.
682
takes_args = ['branch_location?', 'to_location?']
683
takes_options = ['revision', # , 'basis']
684
Option('lightweight',
685
help="perform a lightweight checkout. Lightweight "
686
"checkouts depend on access to the branch for "
687
"every operation. Normal checkouts can perform "
688
"common operations like diff and status without "
689
"such access, and also support local commits."
693
def run(self, branch_location=None, to_location=None, revision=None, basis=None,
697
elif len(revision) > 1:
698
raise BzrCommandError(
699
'bzr checkout --revision takes exactly 1 revision value')
700
if branch_location is None:
701
branch_location = bzrlib.osutils.getcwd()
702
to_location = branch_location
703
source = Branch.open(branch_location)
704
if len(revision) == 1 and revision[0] is not None:
705
revision_id = revision[0].in_history(source)[1]
708
if to_location is None:
709
to_location = os.path.basename(branch_location.rstrip("/\\"))
710
# if the source and to_location are the same,
711
# and there is no working tree,
712
# then reconstitute a branch
713
if (bzrlib.osutils.abspath(to_location) ==
714
bzrlib.osutils.abspath(branch_location)):
716
source.bzrdir.open_workingtree()
717
except errors.NoWorkingTree:
718
source.bzrdir.create_workingtree()
721
os.mkdir(to_location)
723
if e.errno == errno.EEXIST:
724
raise BzrCommandError('Target directory "%s" already'
725
' exists.' % to_location)
726
if e.errno == errno.ENOENT:
727
raise BzrCommandError('Parent of "%s" does not exist.' %
731
old_format = bzrlib.bzrdir.BzrDirFormat.get_default_format()
732
bzrlib.bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
735
checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
736
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
738
checkout_branch = bzrlib.bzrdir.BzrDir.create_branch_convenience(
739
to_location, force_new_tree=False)
740
checkout = checkout_branch.bzrdir
741
checkout_branch.bind(source)
742
if revision_id is not None:
743
rh = checkout_branch.revision_history()
744
checkout_branch.set_revision_history(rh[:rh.index(revision_id) + 1])
745
checkout.create_workingtree(revision_id)
747
bzrlib.bzrdir.BzrDirFormat.set_default_format(old_format)
750
class cmd_renames(Command):
751
"""Show list of renamed files.
753
# TODO: Option to show renames between two historical versions.
755
# TODO: Only show renames under dir, rather than in the whole branch.
756
takes_args = ['dir?']
759
def run(self, dir=u'.'):
760
tree = WorkingTree.open_containing(dir)[0]
761
old_inv = tree.basis_tree().inventory
762
new_inv = tree.read_working_inventory()
764
renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
766
for old_name, new_name in renames:
767
self.outf.write("%s => %s\n" % (old_name, new_name))
770
class cmd_update(Command):
771
"""Update a tree to have the latest code committed to its branch.
773
This will perform a merge into the working tree, and may generate
774
conflicts. If you have any local changes, you will still
775
need to commit them after the update for the update to be complete.
777
If you want to discard your local changes, you can just do a
778
'bzr revert' instead of 'bzr commit' after the update.
780
takes_args = ['dir?']
782
def run(self, dir='.'):
783
tree = WorkingTree.open_containing(dir)[0]
786
if tree.last_revision() == tree.branch.last_revision():
787
# may be up to date, check master too.
788
master = tree.branch.get_master_branch()
789
if master is None or master.last_revision == tree.last_revision():
790
note("Tree is up to date.")
792
conflicts = tree.update()
793
note('Updated to revision %d.' %
794
(tree.branch.revision_id_to_revno(tree.last_revision()),))
803
class cmd_info(Command):
804
"""Show information about a working tree, branch or repository.
806
This command will show all known locations and formats associated to the
807
tree, branch or repository. Statistical information is included with
810
Branches and working trees will also report any missing revisions.
812
takes_args = ['location?']
813
takes_options = ['verbose']
816
def run(self, location=None, verbose=False):
817
from bzrlib.info import show_bzrdir_info
818
show_bzrdir_info(bzrdir.BzrDir.open_containing(location)[0],
822
class cmd_remove(Command):
823
"""Make a file unversioned.
825
This makes bzr stop tracking changes to a versioned file. It does
826
not delete the working copy.
828
takes_args = ['file+']
829
takes_options = ['verbose']
832
def run(self, file_list, verbose=False):
833
tree, file_list = tree_files(file_list)
834
tree.remove(file_list, verbose=verbose)
837
class cmd_file_id(Command):
838
"""Print file_id of a particular file or directory.
840
The file_id is assigned when the file is first added and remains the
841
same through all revisions where the file exists, even when it is
845
takes_args = ['filename']
848
def run(self, filename):
849
tree, relpath = WorkingTree.open_containing(filename)
850
i = tree.inventory.path2id(relpath)
852
raise BzrError("%r is not a versioned file" % filename)
855
self.outf.write('\n')
858
class cmd_file_path(Command):
859
"""Print path of file_ids to a file or directory.
861
This prints one line for each directory down to the target,
862
starting at the branch root.
865
takes_args = ['filename']
868
def run(self, filename):
869
tree, relpath = WorkingTree.open_containing(filename)
871
fid = inv.path2id(relpath)
873
raise BzrError("%r is not a versioned file" % filename)
874
for fip in inv.get_idpath(fid):
876
self.outf.write('\n')
879
class cmd_reconcile(Command):
880
"""Reconcile bzr metadata in a branch.
882
This can correct data mismatches that may have been caused by
883
previous ghost operations or bzr upgrades. You should only
884
need to run this command if 'bzr check' or a bzr developer
885
advises you to run it.
887
If a second branch is provided, cross-branch reconciliation is
888
also attempted, which will check that data like the tree root
889
id which was not present in very early bzr versions is represented
890
correctly in both branches.
892
At the same time it is run it may recompress data resulting in
893
a potential saving in disk space or performance gain.
895
The branch *MUST* be on a listable system such as local disk or sftp.
897
takes_args = ['branch?']
899
def run(self, branch="."):
900
from bzrlib.reconcile import reconcile
901
dir = bzrlib.bzrdir.BzrDir.open(branch)
905
class cmd_revision_history(Command):
906
"""Display list of revision ids on this branch."""
911
branch = WorkingTree.open_containing(u'.')[0].branch
912
for patchid in branch.revision_history():
913
self.outf.write(patchid)
914
self.outf.write('\n')
917
class cmd_ancestry(Command):
918
"""List all revisions merged into this branch."""
923
tree = WorkingTree.open_containing(u'.')[0]
925
# FIXME. should be tree.last_revision
926
for revision_id in b.repository.get_ancestry(b.last_revision()):
927
if revision_id is None:
929
self.outf.write(revision_id)
930
self.outf.write('\n')
933
class cmd_init(Command):
934
"""Make a directory into a versioned branch.
936
Use this to create an empty branch, or before importing an
939
If there is a repository in a parent directory of the location, then
940
the history of the branch will be stored in the repository. Otherwise
941
init creates a standalone branch which carries its own history in
944
If there is already a branch at the location but it has no working tree,
945
the tree can be populated with 'bzr checkout'.
947
Recipe for importing a tree of files:
952
bzr commit -m 'imported project'
954
takes_args = ['location?']
957
help='Specify a format for this branch. Current'
958
' formats are: default, knit, metaweave and'
959
' weave. Default is knit; metaweave and'
960
' weave are deprecated',
961
type=get_format_type),
963
def run(self, location=None, format=None):
964
from bzrlib.branch import Branch
966
format = get_format_type('default')
970
# The path has to exist to initialize a
971
# branch inside of it.
972
# Just using os.mkdir, since I don't
973
# believe that we want to create a bunch of
974
# locations if the user supplies an extended path
975
if not os.path.exists(location):
978
existing_bzrdir = bzrdir.BzrDir.open(location)
979
except NotBranchError:
980
# really a NotBzrDir error...
981
bzrdir.BzrDir.create_branch_convenience(location, format=format)
983
if existing_bzrdir.has_branch():
984
if existing_bzrdir.has_workingtree():
985
raise errors.AlreadyBranchError(location)
987
raise errors.BranchExistsWithoutWorkingTree(location)
989
existing_bzrdir.create_branch()
990
existing_bzrdir.create_workingtree()
993
class cmd_init_repository(Command):
994
"""Create a shared repository to hold branches.
996
New branches created under the repository directory will store their revisions
997
in the repository, not in the branch directory, if the branch format supports
1003
bzr checkout --lightweight repo/trunk trunk-checkout
1007
takes_args = ["location"]
1008
takes_options = [Option('format',
1009
help='Specify a format for this repository.'
1010
' Current formats are: default, knit,'
1011
' metaweave and weave. Default is knit;'
1012
' metaweave and weave are deprecated',
1013
type=get_format_type),
1015
help='Allows branches in repository to have'
1017
aliases = ["init-repo"]
1018
def run(self, location, format=None, trees=False):
1019
from bzrlib.transport import get_transport
1021
format = get_format_type('default')
1022
transport = get_transport(location)
1023
if not transport.has('.'):
1025
newdir = format.initialize_on_transport(transport)
1026
repo = newdir.create_repository(shared=True)
1027
repo.set_make_working_trees(trees)
1030
class cmd_diff(Command):
1031
"""Show differences in working tree.
1033
If files are listed, only the changes in those files are listed.
1034
Otherwise, all changes for the tree are listed.
1036
"bzr diff -p1" is equivalent to "bzr diff --prefix old/:new/", and
1037
produces patches suitable for "patch -p1".
1043
bzr diff --diff-prefix old/:new/
1044
bzr diff bzr.mine bzr.dev
1047
# TODO: Option to use external diff command; could be GNU diff, wdiff,
1048
# or a graphical diff.
1050
# TODO: Python difflib is not exactly the same as unidiff; should
1051
# either fix it up or prefer to use an external diff.
1053
# TODO: Selected-file diff is inefficient and doesn't show you
1056
# TODO: This probably handles non-Unix newlines poorly.
1058
takes_args = ['file*']
1059
takes_options = ['revision', 'diff-options', 'prefix']
1060
aliases = ['di', 'dif']
1061
encoding_type = 'exact'
1064
def run(self, revision=None, file_list=None, diff_options=None,
1066
from bzrlib.diff import diff_cmd_helper, show_diff_trees
1068
if (prefix is None) or (prefix == '0'):
1076
if not ':' in prefix:
1077
raise BzrError("--diff-prefix expects two values separated by a colon")
1078
old_label, new_label = prefix.split(":")
1081
tree1, file_list = internal_tree_files(file_list)
1085
except FileInWrongBranch:
1086
if len(file_list) != 2:
1087
raise BzrCommandError("Files are in different branches")
1089
tree1, file1 = WorkingTree.open_containing(file_list[0])
1090
tree2, file2 = WorkingTree.open_containing(file_list[1])
1091
if file1 != "" or file2 != "":
1092
# FIXME diff those two files. rbc 20051123
1093
raise BzrCommandError("Files are in different branches")
1095
if revision is not None:
1096
if tree2 is not None:
1097
raise BzrCommandError("Can't specify -r with two branches")
1098
if (len(revision) == 1) or (revision[1].spec is None):
1099
return diff_cmd_helper(tree1, file_list, diff_options,
1101
old_label=old_label, new_label=new_label)
1102
elif len(revision) == 2:
1103
return diff_cmd_helper(tree1, file_list, diff_options,
1104
revision[0], revision[1],
1105
old_label=old_label, new_label=new_label)
1107
raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
1109
if tree2 is not None:
1110
return show_diff_trees(tree1, tree2, sys.stdout,
1111
specific_files=file_list,
1112
external_diff_options=diff_options,
1113
old_label=old_label, new_label=new_label)
1115
return diff_cmd_helper(tree1, file_list, diff_options,
1116
old_label=old_label, new_label=new_label)
1119
class cmd_deleted(Command):
1120
"""List files deleted in the working tree.
1122
# TODO: Show files deleted since a previous revision, or
1123
# between two revisions.
1124
# TODO: Much more efficient way to do this: read in new
1125
# directories with readdir, rather than stating each one. Same
1126
# level of effort but possibly much less IO. (Or possibly not,
1127
# if the directories are very large...)
1128
takes_options = ['show-ids']
1131
def run(self, show_ids=False):
1132
tree = WorkingTree.open_containing(u'.')[0]
1133
old = tree.basis_tree()
1134
for path, ie in old.inventory.iter_entries():
1135
if not tree.has_id(ie.file_id):
1136
self.outf.write(path)
1138
self.outf.write(' ')
1139
self.outf.write(ie.file_id)
1140
self.outf.write('\n')
1143
class cmd_modified(Command):
1144
"""List files modified in working tree."""
1148
from bzrlib.delta import compare_trees
1150
tree = WorkingTree.open_containing(u'.')[0]
1151
td = compare_trees(tree.basis_tree(), tree)
1153
for path, id, kind, text_modified, meta_modified in td.modified:
1154
self.outf.write(path)
1155
self.outf.write('\n')
1158
class cmd_added(Command):
1159
"""List files added in working tree."""
1163
wt = WorkingTree.open_containing(u'.')[0]
1164
basis_inv = wt.basis_tree().inventory
1167
if file_id in basis_inv:
1169
path = inv.id2path(file_id)
1170
if not os.access(bzrlib.osutils.abspath(path), os.F_OK):
1172
self.outf.write(path)
1173
self.outf.write('\n')
1176
class cmd_root(Command):
1177
"""Show the tree root directory.
1179
The root is the nearest enclosing directory with a .bzr control
1181
takes_args = ['filename?']
1183
def run(self, filename=None):
1184
"""Print the branch root."""
1185
tree = WorkingTree.open_containing(filename)[0]
1186
self.outf.write(tree.basedir)
1187
self.outf.write('\n')
1190
class cmd_log(Command):
1191
"""Show log of a branch, file, or directory.
1193
By default show the log of the branch containing the working directory.
1195
To request a range of logs, you can use the command -r begin..end
1196
-r revision requests a specific revision, -r ..end or -r begin.. are
1202
bzr log -r -10.. http://server/branch
1205
# TODO: Make --revision support uuid: and hash: [future tag:] notation.
1207
takes_args = ['location?']
1208
takes_options = [Option('forward',
1209
help='show from oldest to newest'),
1212
help='show files changed in each revision'),
1213
'show-ids', 'revision',
1217
help='show revisions whose message matches this regexp',
1221
encoding_type = 'replace'
1224
def run(self, location=None, timezone='original',
1234
from bzrlib.log import log_formatter, show_log
1235
assert message is None or isinstance(message, basestring), \
1236
"invalid message argument %r" % message
1237
direction = (forward and 'forward') or 'reverse'
1242
# find the file id to log:
1244
dir, fp = bzrdir.BzrDir.open_containing(location)
1245
b = dir.open_branch()
1249
inv = dir.open_workingtree().inventory
1250
except (errors.NotBranchError, errors.NotLocalUrl):
1251
# either no tree, or is remote.
1252
inv = b.basis_tree().inventory
1253
file_id = inv.path2id(fp)
1256
# FIXME ? log the current subdir only RBC 20060203
1257
dir, relpath = bzrdir.BzrDir.open_containing('.')
1258
b = dir.open_branch()
1260
if revision is None:
1263
elif len(revision) == 1:
1264
rev1 = rev2 = revision[0].in_history(b).revno
1265
elif len(revision) == 2:
1266
if revision[0].spec is None:
1267
# missing begin-range means first revision
1270
rev1 = revision[0].in_history(b).revno
1272
if revision[1].spec is None:
1273
# missing end-range means last known revision
1276
rev2 = revision[1].in_history(b).revno
1278
raise BzrCommandError('bzr log --revision takes one or two values.')
1280
# By this point, the revision numbers are converted to the +ve
1281
# form if they were supplied in the -ve form, so we can do
1282
# this comparison in relative safety
1284
(rev2, rev1) = (rev1, rev2)
1286
if (log_format == None):
1287
default = bzrlib.config.BranchConfig(b).log_format()
1288
log_format = get_log_format(long=long, short=short, line=line, default=default)
1289
lf = log_formatter(log_format,
1292
show_timezone=timezone)
1298
direction=direction,
1299
start_revision=rev1,
1304
def get_log_format(long=False, short=False, line=False, default='long'):
1305
log_format = default
1309
log_format = 'short'
1315
class cmd_touching_revisions(Command):
1316
"""Return revision-ids which affected a particular file.
1318
A more user-friendly interface is "bzr log FILE"."""
1320
takes_args = ["filename"]
1321
encoding_type = 'replace'
1324
def run(self, filename):
1325
tree, relpath = WorkingTree.open_containing(filename)
1327
inv = tree.read_working_inventory()
1328
file_id = inv.path2id(relpath)
1329
for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1330
self.outf.write("%6d %s\n" % (revno, what))
1333
class cmd_ls(Command):
1334
"""List files in a tree.
1336
# TODO: Take a revision or remote path and list that tree instead.
1338
takes_options = ['verbose', 'revision',
1339
Option('non-recursive',
1340
help='don\'t recurse into sub-directories'),
1342
help='Print all paths from the root of the branch.'),
1343
Option('unknown', help='Print unknown files'),
1344
Option('versioned', help='Print versioned files'),
1345
Option('ignored', help='Print ignored files'),
1347
Option('null', help='Null separate the files'),
1350
def run(self, revision=None, verbose=False,
1351
non_recursive=False, from_root=False,
1352
unknown=False, versioned=False, ignored=False,
1355
if verbose and null:
1356
raise BzrCommandError('Cannot set both --verbose and --null')
1357
all = not (unknown or versioned or ignored)
1359
selection = {'I':ignored, '?':unknown, 'V':versioned}
1361
tree, relpath = WorkingTree.open_containing(u'.')
1366
if revision is not None:
1367
tree = tree.branch.repository.revision_tree(
1368
revision[0].in_history(tree.branch).rev_id)
1370
for fp, fc, kind, fid, entry in tree.list_files():
1371
if fp.startswith(relpath):
1372
fp = fp[len(relpath):]
1373
if non_recursive and '/' in fp:
1375
if not all and not selection[fc]:
1378
kindch = entry.kind_character()
1379
self.outf.write('%-8s %s%s\n' % (fc, fp, kindch))
1382
self.outf.write('\0')
1386
self.outf.write('\n')
1389
class cmd_unknowns(Command):
1390
"""List unknown files."""
1393
from bzrlib.osutils import quotefn
1394
for f in WorkingTree.open_containing(u'.')[0].unknowns():
1395
self.outf.write(quotefn(f))
1396
self.outf.write('\n')
1399
class cmd_ignore(Command):
1400
"""Ignore a command or pattern.
1402
To remove patterns from the ignore list, edit the .bzrignore file.
1404
If the pattern contains a slash, it is compared to the whole path
1405
from the branch root. Otherwise, it is compared to only the last
1406
component of the path. To match a file only in the root directory,
1409
Ignore patterns are case-insensitive on case-insensitive systems.
1411
Note: wildcards must be quoted from the shell on Unix.
1414
bzr ignore ./Makefile
1415
bzr ignore '*.class'
1417
# TODO: Complain if the filename is absolute
1418
takes_args = ['name_pattern']
1420
def run(self, name_pattern):
1421
from bzrlib.atomicfile import AtomicFile
1424
tree, relpath = WorkingTree.open_containing(u'.')
1425
ifn = tree.abspath('.bzrignore')
1427
if os.path.exists(ifn):
1430
igns = f.read().decode('utf-8')
1436
# TODO: If the file already uses crlf-style termination, maybe
1437
# we should use that for the newly added lines?
1439
if igns and igns[-1] != '\n':
1441
igns += name_pattern + '\n'
1444
f = AtomicFile(ifn, 'wt')
1445
f.write(igns.encode('utf-8'))
1450
inv = tree.inventory
1451
if inv.path2id('.bzrignore'):
1452
mutter('.bzrignore is already versioned')
1454
mutter('need to make new .bzrignore file versioned')
1455
tree.add(['.bzrignore'])
1458
class cmd_ignored(Command):
1459
"""List ignored files and the patterns that matched them.
1461
See also: bzr ignore"""
1464
tree = WorkingTree.open_containing(u'.')[0]
1465
for path, file_class, kind, file_id, entry in tree.list_files():
1466
if file_class != 'I':
1468
## XXX: Slightly inefficient since this was already calculated
1469
pat = tree.is_ignored(path)
1470
print '%-50s %s' % (path, pat)
1473
class cmd_lookup_revision(Command):
1474
"""Lookup the revision-id from a revision-number
1477
bzr lookup-revision 33
1480
takes_args = ['revno']
1483
def run(self, revno):
1487
raise BzrCommandError("not a valid revision-number: %r" % revno)
1489
print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
1492
class cmd_export(Command):
1493
"""Export past revision to destination directory.
1495
If no revision is specified this exports the last committed revision.
1497
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
1498
given, try to find the format with the extension. If no extension
1499
is found exports to a directory (equivalent to --format=dir).
1501
Root may be the top directory for tar, tgz and tbz2 formats. If none
1502
is given, the top directory will be the root name of the file.
1504
Note: export of tree with non-ascii filenames to zip is not supported.
1506
Supported formats Autodetected by extension
1507
----------------- -------------------------
1510
tbz2 .tar.bz2, .tbz2
1514
takes_args = ['dest']
1515
takes_options = ['revision', 'format', 'root']
1516
def run(self, dest, revision=None, format=None, root=None):
1518
from bzrlib.export import export
1519
tree = WorkingTree.open_containing(u'.')[0]
1521
if revision is None:
1522
# should be tree.last_revision FIXME
1523
rev_id = b.last_revision()
1525
if len(revision) != 1:
1526
raise BzrError('bzr export --revision takes exactly 1 argument')
1527
rev_id = revision[0].in_history(b).rev_id
1528
t = b.repository.revision_tree(rev_id)
1530
export(t, dest, format, root)
1531
except errors.NoSuchExportFormat, e:
1532
raise BzrCommandError('Unsupported export format: %s' % e.format)
1535
class cmd_cat(Command):
1536
"""Write a file's text from a previous revision."""
1538
takes_options = ['revision']
1539
takes_args = ['filename']
1542
def run(self, filename, revision=None):
1543
if revision is not None and len(revision) != 1:
1544
raise BzrCommandError("bzr cat --revision takes exactly one number")
1547
tree, relpath = WorkingTree.open_containing(filename)
1549
except NotBranchError:
1553
b, relpath = Branch.open_containing(filename)
1554
if revision is None:
1555
revision_id = b.last_revision()
1557
revision_id = revision[0].in_history(b).rev_id
1558
b.print_file(relpath, revision_id)
1561
class cmd_local_time_offset(Command):
1562
"""Show the offset in seconds from GMT to local time."""
1566
print bzrlib.osutils.local_time_offset()
1570
class cmd_commit(Command):
1571
"""Commit changes into a new revision.
1573
If no arguments are given, the entire tree is committed.
1575
If selected files are specified, only changes to those files are
1576
committed. If a directory is specified then the directory and everything
1577
within it is committed.
1579
A selected-file commit may fail in some cases where the committed
1580
tree would be invalid, such as trying to commit a file in a
1581
newly-added directory that is not itself committed.
1583
# TODO: Run hooks on tree to-be-committed, and after commit.
1585
# TODO: Strict commit that fails if there are deleted files.
1586
# (what does "deleted files" mean ??)
1588
# TODO: Give better message for -s, --summary, used by tla people
1590
# XXX: verbose currently does nothing
1592
takes_args = ['selected*']
1593
takes_options = ['message', 'verbose',
1595
help='commit even if nothing has changed'),
1596
Option('file', type=str,
1598
help='file containing commit message'),
1600
help="refuse to commit if there are unknown "
1601
"files in the working tree."),
1603
help="perform a local only commit in a bound "
1604
"branch. Such commits are not pushed to "
1605
"the master branch until a normal commit "
1609
aliases = ['ci', 'checkin']
1611
def run(self, message=None, file=None, verbose=True, selected_list=None,
1612
unchanged=False, strict=False, local=False):
1613
from bzrlib.commit import (NullCommitReporter, ReportCommitToLog)
1614
from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1616
from bzrlib.msgeditor import edit_commit_message, \
1617
make_commit_message_template
1618
from tempfile import TemporaryFile
1620
# TODO: Need a blackbox test for invoking the external editor; may be
1621
# slightly problematic to run this cross-platform.
1623
# TODO: do more checks that the commit will succeed before
1624
# spending the user's valuable time typing a commit message.
1626
# TODO: if the commit *does* happen to fail, then save the commit
1627
# message to a temporary file where it can be recovered
1628
tree, selected_list = tree_files(selected_list)
1629
if local and not tree.branch.get_bound_location():
1630
raise errors.LocalRequiresBoundBranch()
1631
if message is None and not file:
1632
template = make_commit_message_template(tree, selected_list)
1633
message = edit_commit_message(template)
1635
raise BzrCommandError("please specify a commit message"
1636
" with either --message or --file")
1637
elif message and file:
1638
raise BzrCommandError("please specify either --message or --file")
1641
message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1644
raise BzrCommandError("empty commit message specified")
1647
reporter = ReportCommitToLog()
1649
reporter = NullCommitReporter()
1652
tree.commit(message, specific_files=selected_list,
1653
allow_pointless=unchanged, strict=strict, local=local,
1655
except PointlessCommit:
1656
# FIXME: This should really happen before the file is read in;
1657
# perhaps prepare the commit; get the message; then actually commit
1658
raise BzrCommandError("no changes to commit",
1659
["use --unchanged to commit anyhow"])
1660
except ConflictsInTree:
1661
raise BzrCommandError("Conflicts detected in working tree. "
1662
'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1663
except StrictCommitFailed:
1664
raise BzrCommandError("Commit refused because there are unknown "
1665
"files in the working tree.")
1666
except errors.BoundBranchOutOfDate, e:
1667
raise BzrCommandError(str(e)
1668
+ ' Either unbind, update, or'
1669
' pass --local to commit.')
1672
class cmd_check(Command):
1673
"""Validate consistency of branch history.
1675
This command checks various invariants about the branch storage to
1676
detect data corruption or bzr bugs.
1678
takes_args = ['branch?']
1679
takes_options = ['verbose']
1681
def run(self, branch=None, verbose=False):
1682
from bzrlib.check import check
1684
tree = WorkingTree.open_containing()[0]
1685
branch = tree.branch
1687
branch = Branch.open(branch)
1688
check(branch, verbose)
1691
class cmd_scan_cache(Command):
1694
from bzrlib.hashcache import HashCache
1700
print '%6d stats' % c.stat_count
1701
print '%6d in hashcache' % len(c._cache)
1702
print '%6d files removed from cache' % c.removed_count
1703
print '%6d hashes updated' % c.update_count
1704
print '%6d files changed too recently to cache' % c.danger_count
1710
class cmd_upgrade(Command):
1711
"""Upgrade branch storage to current format.
1713
The check command or bzr developers may sometimes advise you to run
1714
this command. When the default format has changed you may also be warned
1715
during other operations to upgrade.
1717
takes_args = ['url?']
1720
help='Upgrade to a specific format. Current formats'
1721
' are: default, knit, metaweave and weave.'
1722
' Default is knit; metaweave and weave are'
1724
type=get_format_type),
1728
def run(self, url='.', format=None):
1729
from bzrlib.upgrade import upgrade
1731
format = get_format_type('default')
1732
upgrade(url, format)
1735
class cmd_whoami(Command):
1736
"""Show bzr user id."""
1737
takes_options = ['email']
1740
def run(self, email=False):
1742
b = WorkingTree.open_containing(u'.')[0].branch
1743
config = bzrlib.config.BranchConfig(b)
1744
except NotBranchError:
1745
config = bzrlib.config.GlobalConfig()
1748
print config.user_email()
1750
print config.username()
1753
class cmd_nick(Command):
1754
"""Print or set the branch nickname.
1756
If unset, the tree root directory name is used as the nickname
1757
To print the current nickname, execute with no argument.
1759
takes_args = ['nickname?']
1760
def run(self, nickname=None):
1761
branch = Branch.open_containing(u'.')[0]
1762
if nickname is None:
1763
self.printme(branch)
1765
branch.nick = nickname
1768
def printme(self, branch):
1772
class cmd_selftest(Command):
1773
"""Run internal test suite.
1775
This creates temporary test directories in the working directory,
1776
but not existing data is affected. These directories are deleted
1777
if the tests pass, or left behind to help in debugging if they
1778
fail and --keep-output is specified.
1780
If arguments are given, they are regular expressions that say
1781
which tests should run.
1783
If the global option '--no-plugins' is given, plugins are not loaded
1784
before running the selftests. This has two effects: features provided or
1785
modified by plugins will not be tested, and tests provided by plugins will
1790
bzr --no-plugins selftest -v
1792
# TODO: --list should give a list of all available tests
1794
# NB: this is used from the class without creating an instance, which is
1795
# why it does not have a self parameter.
1796
def get_transport_type(typestring):
1797
"""Parse and return a transport specifier."""
1798
if typestring == "sftp":
1799
from bzrlib.transport.sftp import SFTPAbsoluteServer
1800
return SFTPAbsoluteServer
1801
if typestring == "memory":
1802
from bzrlib.transport.memory import MemoryServer
1804
if typestring == "fakenfs":
1805
from bzrlib.transport.fakenfs import FakeNFSServer
1806
return FakeNFSServer
1807
msg = "No known transport type %s. Supported types are: sftp\n" %\
1809
raise BzrCommandError(msg)
1812
takes_args = ['testspecs*']
1813
takes_options = ['verbose',
1814
Option('one', help='stop when one test fails'),
1815
Option('keep-output',
1816
help='keep output directories when tests fail'),
1818
help='Use a different transport by default '
1819
'throughout the test suite.',
1820
type=get_transport_type),
1823
def run(self, testspecs_list=None, verbose=False, one=False,
1824
keep_output=False, transport=None):
1826
from bzrlib.tests import selftest
1827
# we don't want progress meters from the tests to go to the
1828
# real output; and we don't want log messages cluttering up
1830
save_ui = bzrlib.ui.ui_factory
1831
bzrlib.trace.info('running tests...')
1833
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1834
if testspecs_list is not None:
1835
pattern = '|'.join(testspecs_list)
1838
result = selftest(verbose=verbose,
1840
stop_on_failure=one,
1841
keep_output=keep_output,
1842
transport=transport)
1844
bzrlib.trace.info('tests passed')
1846
bzrlib.trace.info('tests failed')
1847
return int(not result)
1849
bzrlib.ui.ui_factory = save_ui
1852
def _get_bzr_branch():
1853
"""If bzr is run from a branch, return Branch or None"""
1854
import bzrlib.errors
1855
from bzrlib.branch import Branch
1856
from bzrlib.osutils import abspath
1857
from os.path import dirname
1860
branch = Branch.open(dirname(abspath(dirname(__file__))))
1862
except bzrlib.errors.BzrError:
1867
print "bzr (bazaar-ng) %s" % bzrlib.__version__
1868
# is bzrlib itself in a branch?
1869
branch = _get_bzr_branch()
1871
rh = branch.revision_history()
1873
print " bzr checkout, revision %d" % (revno,)
1874
print " nick: %s" % (branch.nick,)
1876
print " revid: %s" % (rh[-1],)
1877
print bzrlib.__copyright__
1878
print "http://bazaar-ng.org/"
1880
print "bzr comes with ABSOLUTELY NO WARRANTY. bzr is free software, and"
1881
print "you may use, modify and redistribute it under the terms of the GNU"
1882
print "General Public License version 2 or later."
1885
class cmd_version(Command):
1886
"""Show version of bzr."""
1891
class cmd_rocks(Command):
1892
"""Statement of optimism."""
1896
print "it sure does!"
1899
class cmd_find_merge_base(Command):
1900
"""Find and print a base revision for merging two branches.
1902
# TODO: Options to specify revisions on either side, as if
1903
# merging only part of the history.
1904
takes_args = ['branch', 'other']
1908
def run(self, branch, other):
1909
from bzrlib.revision import common_ancestor, MultipleRevisionSources
1911
branch1 = Branch.open_containing(branch)[0]
1912
branch2 = Branch.open_containing(other)[0]
1914
history_1 = branch1.revision_history()
1915
history_2 = branch2.revision_history()
1917
last1 = branch1.last_revision()
1918
last2 = branch2.last_revision()
1920
source = MultipleRevisionSources(branch1.repository,
1923
base_rev_id = common_ancestor(last1, last2, source)
1925
print 'merge base is revision %s' % base_rev_id
1929
if base_revno is None:
1930
raise bzrlib.errors.UnrelatedBranches()
1932
print ' r%-6d in %s' % (base_revno, branch)
1934
other_revno = branch2.revision_id_to_revno(base_revid)
1936
print ' r%-6d in %s' % (other_revno, other)
1940
class cmd_merge(Command):
1941
"""Perform a three-way merge.
1943
The branch is the branch you will merge from. By default, it will
1944
merge the latest revision. If you specify a revision, that
1945
revision will be merged. If you specify two revisions, the first
1946
will be used as a BASE, and the second one as OTHER. Revision
1947
numbers are always relative to the specified branch.
1949
By default, bzr will try to merge in all new work from the other
1950
branch, automatically determining an appropriate base. If this
1951
fails, you may need to give an explicit base.
1953
Merge will do its best to combine the changes in two branches, but there
1954
are some kinds of problems only a human can fix. When it encounters those,
1955
it will mark a conflict. A conflict means that you need to fix something,
1956
before you should commit.
1958
Use bzr resolve when you have fixed a problem. See also bzr conflicts.
1960
If there is no default branch set, the first merge will set it. After
1961
that, you can omit the branch to use the default. To change the
1962
default, use --remember.
1966
To merge the latest revision from bzr.dev
1967
bzr merge ../bzr.dev
1969
To merge changes up to and including revision 82 from bzr.dev
1970
bzr merge -r 82 ../bzr.dev
1972
To merge the changes introduced by 82, without previous changes:
1973
bzr merge -r 81..82 ../bzr.dev
1975
merge refuses to run if there are any uncommitted changes, unless
1978
takes_args = ['branch?']
1979
takes_options = ['revision', 'force', 'merge-type', 'reprocess', 'remember',
1980
Option('show-base', help="Show base revision text in "
1983
def run(self, branch=None, revision=None, force=False, merge_type=None,
1984
show_base=False, reprocess=False, remember=False):
1985
if merge_type is None:
1986
merge_type = Merge3Merger
1988
tree = WorkingTree.open_containing(u'.')[0]
1989
stored_loc = tree.branch.get_parent()
1991
if stored_loc is None:
1992
raise BzrCommandError("No merge branch known or specified.")
1994
print (u"Using saved branch: %s"
1995
% urlutils.unescape_for_display(stored_loc))
1998
if revision is None or len(revision) < 1:
2000
other = [branch, -1]
2001
other_branch, path = Branch.open_containing(branch)
2003
if len(revision) == 1:
2005
other_branch, path = Branch.open_containing(branch)
2006
revno = revision[0].in_history(other_branch).revno
2007
other = [branch, revno]
2009
assert len(revision) == 2
2010
if None in revision:
2011
raise BzrCommandError(
2012
"Merge doesn't permit that revision specifier.")
2013
other_branch, path = Branch.open_containing(branch)
2015
base = [branch, revision[0].in_history(other_branch).revno]
2016
other = [branch, revision[1].in_history(other_branch).revno]
2018
if tree.branch.get_parent() is None or remember:
2019
tree.branch.set_parent(other_branch.base)
2022
interesting_files = [path]
2024
interesting_files = None
2025
pb = bzrlib.ui.ui_factory.nested_progress_bar()
2028
conflict_count = merge(other, base, check_clean=(not force),
2029
merge_type=merge_type,
2030
reprocess=reprocess,
2031
show_base=show_base,
2032
pb=pb, file_list=interesting_files)
2035
if conflict_count != 0:
2039
except bzrlib.errors.AmbiguousBase, e:
2040
m = ("sorry, bzr can't determine the right merge base yet\n"
2041
"candidates are:\n "
2042
+ "\n ".join(e.bases)
2044
"please specify an explicit base with -r,\n"
2045
"and (if you want) report this to the bzr developers\n")
2049
class cmd_remerge(Command):
2052
takes_args = ['file*']
2053
takes_options = ['merge-type', 'reprocess',
2054
Option('show-base', help="Show base revision text in "
2057
def run(self, file_list=None, merge_type=None, show_base=False,
2059
from bzrlib.merge import merge_inner, transform_tree
2060
if merge_type is None:
2061
merge_type = Merge3Merger
2062
tree, file_list = tree_files(file_list)
2065
pending_merges = tree.pending_merges()
2066
if len(pending_merges) != 1:
2067
raise BzrCommandError("Sorry, remerge only works after normal"
2068
+ " merges. Not cherrypicking or"
2070
repository = tree.branch.repository
2071
base_revision = common_ancestor(tree.branch.last_revision(),
2072
pending_merges[0], repository)
2073
base_tree = repository.revision_tree(base_revision)
2074
other_tree = repository.revision_tree(pending_merges[0])
2075
interesting_ids = None
2076
if file_list is not None:
2077
interesting_ids = set()
2078
for filename in file_list:
2079
file_id = tree.path2id(filename)
2081
raise NotVersionedError(filename)
2082
interesting_ids.add(file_id)
2083
if tree.kind(file_id) != "directory":
2086
for name, ie in tree.inventory.iter_entries(file_id):
2087
interesting_ids.add(ie.file_id)
2088
transform_tree(tree, tree.basis_tree(), interesting_ids)
2089
if file_list is None:
2090
restore_files = list(tree.iter_conflicts())
2092
restore_files = file_list
2093
for filename in restore_files:
2095
restore(tree.abspath(filename))
2096
except NotConflicted:
2098
conflicts = merge_inner(tree.branch, other_tree, base_tree,
2100
interesting_ids = interesting_ids,
2101
other_rev_id=pending_merges[0],
2102
merge_type=merge_type,
2103
show_base=show_base,
2104
reprocess=reprocess)
2112
class cmd_revert(Command):
2113
"""Reverse all changes since the last commit.
2115
Only versioned files are affected. Specify filenames to revert only
2116
those files. By default, any files that are changed will be backed up
2117
first. Backup files have a '~' appended to their name.
2119
takes_options = ['revision', 'no-backup']
2120
takes_args = ['file*']
2121
aliases = ['merge-revert']
2123
def run(self, revision=None, no_backup=False, file_list=None):
2124
from bzrlib.commands import parse_spec
2125
if file_list is not None:
2126
if len(file_list) == 0:
2127
raise BzrCommandError("No files specified")
2131
tree, file_list = tree_files(file_list)
2132
if revision is None:
2133
# FIXME should be tree.last_revision
2134
rev_id = tree.last_revision()
2135
elif len(revision) != 1:
2136
raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
2138
rev_id = revision[0].in_history(tree.branch).rev_id
2139
pb = bzrlib.ui.ui_factory.nested_progress_bar()
2141
tree.revert(file_list,
2142
tree.branch.repository.revision_tree(rev_id),
2148
class cmd_assert_fail(Command):
2149
"""Test reporting of assertion failures"""
2152
assert False, "always fails"
2155
class cmd_help(Command):
2156
"""Show help on a command or other topic.
2158
For a list of all available commands, say 'bzr help commands'."""
2159
takes_options = [Option('long', 'show help on all commands')]
2160
takes_args = ['topic?']
2161
aliases = ['?', '--help', '-?', '-h']
2164
def run(self, topic=None, long=False):
2166
if topic is None and long:
2171
class cmd_shell_complete(Command):
2172
"""Show appropriate completions for context.
2174
For a list of all available commands, say 'bzr shell-complete'."""
2175
takes_args = ['context?']
2180
def run(self, context=None):
2181
import shellcomplete
2182
shellcomplete.shellcomplete(context)
2185
class cmd_fetch(Command):
2186
"""Copy in history from another branch but don't merge it.
2188
This is an internal method used for pull and merge."""
2190
takes_args = ['from_branch', 'to_branch']
2191
def run(self, from_branch, to_branch):
2192
from bzrlib.fetch import Fetcher
2193
from bzrlib.branch import Branch
2194
from_b = Branch.open(from_branch)
2195
to_b = Branch.open(to_branch)
2196
Fetcher(to_b, from_b)
2199
class cmd_missing(Command):
2200
"""Show unmerged/unpulled revisions between two branches.
2202
OTHER_BRANCH may be local or remote."""
2203
takes_args = ['other_branch?']
2204
takes_options = [Option('reverse', 'Reverse the order of revisions'),
2206
'Display changes in the local branch only'),
2207
Option('theirs-only',
2208
'Display changes in the remote branch only'),
2217
def run(self, other_branch=None, reverse=False, mine_only=False,
2218
theirs_only=False, log_format=None, long=False, short=False, line=False,
2219
show_ids=False, verbose=False):
2220
from bzrlib.missing import find_unmerged, iter_log_data
2221
from bzrlib.log import log_formatter
2222
local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
2223
parent = local_branch.get_parent()
2224
if other_branch is None:
2225
other_branch = parent
2226
if other_branch is None:
2227
raise BzrCommandError("No missing location known or specified.")
2228
print "Using last location: " + local_branch.get_parent()
2229
remote_branch = bzrlib.branch.Branch.open(other_branch)
2230
if remote_branch.base == local_branch.base:
2231
remote_branch = local_branch
2232
local_branch.lock_read()
2234
remote_branch.lock_read()
2236
local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
2237
if (log_format == None):
2238
default = bzrlib.config.BranchConfig(local_branch).log_format()
2239
log_format = get_log_format(long=long, short=short, line=line, default=default)
2240
lf = log_formatter(log_format, sys.stdout,
2242
show_timezone='original')
2243
if reverse is False:
2244
local_extra.reverse()
2245
remote_extra.reverse()
2246
if local_extra and not theirs_only:
2247
print "You have %d extra revision(s):" % len(local_extra)
2248
for data in iter_log_data(local_extra, local_branch.repository,
2251
printed_local = True
2253
printed_local = False
2254
if remote_extra and not mine_only:
2255
if printed_local is True:
2257
print "You are missing %d revision(s):" % len(remote_extra)
2258
for data in iter_log_data(remote_extra, remote_branch.repository,
2261
if not remote_extra and not local_extra:
2263
print "Branches are up to date."
2267
remote_branch.unlock()
2269
local_branch.unlock()
2270
if not status_code and parent is None and other_branch is not None:
2271
local_branch.lock_write()
2273
# handle race conditions - a parent might be set while we run.
2274
if local_branch.get_parent() is None:
2275
local_branch.set_parent(remote_branch.base)
2277
local_branch.unlock()
2281
class cmd_plugins(Command):
2286
import bzrlib.plugin
2287
from inspect import getdoc
2288
for name, plugin in bzrlib.plugin.all_plugins().items():
2289
if hasattr(plugin, '__path__'):
2290
print plugin.__path__[0]
2291
elif hasattr(plugin, '__file__'):
2292
print plugin.__file__
2298
print '\t', d.split('\n')[0]
2301
class cmd_testament(Command):
2302
"""Show testament (signing-form) of a revision."""
2303
takes_options = ['revision', 'long']
2304
takes_args = ['branch?']
2306
def run(self, branch=u'.', revision=None, long=False):
2307
from bzrlib.testament import Testament
2308
b = WorkingTree.open_containing(branch)[0].branch
2311
if revision is None:
2312
rev_id = b.last_revision()
2314
rev_id = revision[0].in_history(b).rev_id
2315
t = Testament.from_revision(b.repository, rev_id)
2317
sys.stdout.writelines(t.as_text_lines())
2319
sys.stdout.write(t.as_short_text())
2324
class cmd_annotate(Command):
2325
"""Show the origin of each line in a file.
2327
This prints out the given file with an annotation on the left side
2328
indicating which revision, author and date introduced the change.
2330
If the origin is the same for a run of consecutive lines, it is
2331
shown only at the top, unless the --all option is given.
2333
# TODO: annotate directories; showing when each file was last changed
2334
# TODO: if the working copy is modified, show annotations on that
2335
# with new uncommitted lines marked
2336
aliases = ['blame', 'praise']
2337
takes_args = ['filename']
2338
takes_options = [Option('all', help='show annotations on all lines'),
2339
Option('long', help='show date in annotations'),
2344
def run(self, filename, all=False, long=False, revision=None):
2345
from bzrlib.annotate import annotate_file
2346
tree, relpath = WorkingTree.open_containing(filename)
2347
branch = tree.branch
2350
if revision is None:
2351
revision_id = branch.last_revision()
2352
elif len(revision) != 1:
2353
raise BzrCommandError('bzr annotate --revision takes exactly 1 argument')
2355
revision_id = revision[0].in_history(branch).rev_id
2356
file_id = tree.inventory.path2id(relpath)
2357
tree = branch.repository.revision_tree(revision_id)
2358
file_version = tree.inventory[file_id].revision
2359
annotate_file(branch, file_version, file_id, long, all, sys.stdout)
2364
class cmd_re_sign(Command):
2365
"""Create a digital signature for an existing revision."""
2366
# TODO be able to replace existing ones.
2368
hidden = True # is this right ?
2369
takes_args = ['revision_id*']
2370
takes_options = ['revision']
2372
def run(self, revision_id_list=None, revision=None):
2373
import bzrlib.config as config
2374
import bzrlib.gpg as gpg
2375
if revision_id_list is not None and revision is not None:
2376
raise BzrCommandError('You can only supply one of revision_id or --revision')
2377
if revision_id_list is None and revision is None:
2378
raise BzrCommandError('You must supply either --revision or a revision_id')
2379
b = WorkingTree.open_containing(u'.')[0].branch
2380
gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
2381
if revision_id_list is not None:
2382
for revision_id in revision_id_list:
2383
b.repository.sign_revision(revision_id, gpg_strategy)
2384
elif revision is not None:
2385
if len(revision) == 1:
2386
revno, rev_id = revision[0].in_history(b)
2387
b.repository.sign_revision(rev_id, gpg_strategy)
2388
elif len(revision) == 2:
2389
# are they both on rh- if so we can walk between them
2390
# might be nice to have a range helper for arbitrary
2391
# revision paths. hmm.
2392
from_revno, from_revid = revision[0].in_history(b)
2393
to_revno, to_revid = revision[1].in_history(b)
2394
if to_revid is None:
2395
to_revno = b.revno()
2396
if from_revno is None or to_revno is None:
2397
raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
2398
for revno in range(from_revno, to_revno + 1):
2399
b.repository.sign_revision(b.get_rev_id(revno),
2402
raise BzrCommandError('Please supply either one revision, or a range.')
2405
class cmd_bind(Command):
2406
"""Bind the current branch to a master branch.
2408
After binding, commits must succeed on the master branch
2409
before they are executed on the local one.
2412
takes_args = ['location']
2415
def run(self, location=None):
2416
b, relpath = Branch.open_containing(u'.')
2417
b_other = Branch.open(location)
2420
except DivergedBranches:
2421
raise BzrCommandError('These branches have diverged.'
2422
' Try merging, and then bind again.')
2425
class cmd_unbind(Command):
2426
"""Unbind the current branch from its master branch.
2428
After unbinding, the local branch is considered independent.
2429
All subsequent commits will be local.
2436
b, relpath = Branch.open_containing(u'.')
2438
raise BzrCommandError('Local branch is not bound')
2441
class cmd_uncommit(bzrlib.commands.Command):
2442
"""Remove the last committed revision.
2444
By supplying the --all flag, it will not only remove the entry
2445
from revision_history, but also remove all of the entries in the
2448
--verbose will print out what is being removed.
2449
--dry-run will go through all the motions, but not actually
2452
In the future, uncommit will create a changeset, which can then
2456
# TODO: jam 20060108 Add an option to allow uncommit to remove
2457
# unreferenced information in 'branch-as-repostory' branches.
2458
# TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
2459
# information in shared branches as well.
2460
takes_options = ['verbose', 'revision',
2461
Option('dry-run', help='Don\'t actually make changes'),
2462
Option('force', help='Say yes to all questions.')]
2463
takes_args = ['location?']
2466
def run(self, location=None,
2467
dry_run=False, verbose=False,
2468
revision=None, force=False):
2469
from bzrlib.branch import Branch
2470
from bzrlib.log import log_formatter
2472
from bzrlib.uncommit import uncommit
2474
if location is None:
2476
control, relpath = bzrdir.BzrDir.open_containing(location)
2478
tree = control.open_workingtree()
2480
except (errors.NoWorkingTree, errors.NotLocalUrl):
2482
b = control.open_branch()
2484
if revision is None:
2486
rev_id = b.last_revision()
2488
revno, rev_id = revision[0].in_history(b)
2490
print 'No revisions to uncommit.'
2492
for r in range(revno, b.revno()+1):
2493
rev_id = b.get_rev_id(r)
2494
lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
2495
lf.show(r, b.repository.get_revision(rev_id), None)
2498
print 'Dry-run, pretending to remove the above revisions.'
2500
val = raw_input('Press <enter> to continue')
2502
print 'The above revision(s) will be removed.'
2504
val = raw_input('Are you sure [y/N]? ')
2505
if val.lower() not in ('y', 'yes'):
2509
uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
2513
class cmd_break_lock(Command):
2514
"""Break a dead lock on a repository, branch or working directory.
2516
CAUTION: Locks should only be broken when you are sure that the process
2517
holding the lock has been stopped.
2519
You can get information on what locks are open via the 'bzr info' command.
2524
takes_args = ['location?']
2526
def run(self, location=None, show=False):
2527
if location is None:
2529
control, relpath = bzrdir.BzrDir.open_containing(location)
2531
control.break_lock()
2532
except NotImplementedError:
2537
# command-line interpretation helper for merge-related commands
2538
def merge(other_revision, base_revision,
2539
check_clean=True, ignore_zero=False,
2540
this_dir=None, backup_files=False, merge_type=Merge3Merger,
2541
file_list=None, show_base=False, reprocess=False,
2542
pb=DummyProgress()):
2543
"""Merge changes into a tree.
2546
list(path, revno) Base for three-way merge.
2547
If [None, None] then a base will be automatically determined.
2549
list(path, revno) Other revision for three-way merge.
2551
Directory to merge changes into; '.' by default.
2553
If true, this_dir must have no uncommitted changes before the
2555
ignore_zero - If true, suppress the "zero conflicts" message when
2556
there are no conflicts; should be set when doing something we expect
2557
to complete perfectly.
2558
file_list - If supplied, merge only changes to selected files.
2560
All available ancestors of other_revision and base_revision are
2561
automatically pulled into the branch.
2563
The revno may be -1 to indicate the last revision on the branch, which is
2566
This function is intended for use from the command line; programmatic
2567
clients might prefer to call merge.merge_inner(), which has less magic
2570
from bzrlib.merge import Merger
2571
if this_dir is None:
2573
this_tree = WorkingTree.open_containing(this_dir)[0]
2574
if show_base and not merge_type is Merge3Merger:
2575
raise BzrCommandError("Show-base is not supported for this merge"
2576
" type. %s" % merge_type)
2577
if reprocess and not merge_type.supports_reprocess:
2578
raise BzrCommandError("Conflict reduction is not supported for merge"
2579
" type %s." % merge_type)
2580
if reprocess and show_base:
2581
raise BzrCommandError("Cannot do conflict reduction and show base.")
2583
merger = Merger(this_tree.branch, this_tree=this_tree, pb=pb)
2584
merger.pp = ProgressPhase("Merge phase", 5, pb)
2585
merger.pp.next_phase()
2586
merger.check_basis(check_clean)
2587
merger.set_other(other_revision)
2588
merger.pp.next_phase()
2589
merger.set_base(base_revision)
2590
if merger.base_rev_id == merger.other_rev_id:
2591
note('Nothing to do.')
2593
merger.backup_files = backup_files
2594
merger.merge_type = merge_type
2595
merger.set_interesting_files(file_list)
2596
merger.show_base = show_base
2597
merger.reprocess = reprocess
2598
conflicts = merger.do_merge()
2599
if file_list is None:
2600
merger.set_pending()
2606
# these get imported and then picked up by the scan for cmd_*
2607
# TODO: Some more consistent way to split command definitions across files;
2608
# we do need to load at least some information about them to know of
2609
# aliases. ideally we would avoid loading the implementation until the
2610
# details were needed.
2611
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore
2612
from bzrlib.sign_my_commits import cmd_sign_my_commits
2613
from bzrlib.weave_commands import cmd_weave_list, cmd_weave_join, \
2614
cmd_weave_plan_merge, cmd_weave_merge_text