1
# Copyright (C) 2005-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""builtin brz commands"""
19
from __future__ import absolute_import
27
from . import lazy_import
28
lazy_import.lazy_import(globals(), """
33
branch as _mod_branch,
40
config as _mod_config,
52
revision as _mod_revision,
61
from breezy.branch import Branch
62
from breezy.conflicts import ConflictList
63
from breezy.transport import memory
64
from breezy.smtp_connection import SMTPConnection
65
from breezy.workingtree import WorkingTree
66
from breezy.i18n import gettext, ngettext
69
from .commands import (
71
builtin_command_registry,
81
from .revisionspec import (
92
from .trace import mutter, note, warning, is_quiet, get_verbosity_level
95
def _get_branch_location(control_dir, possible_transports=None):
96
"""Return location of branch for this control dir."""
98
target = control_dir.get_branch_reference()
99
except errors.NotBranchError:
100
return control_dir.root_transport.base
101
if target is not None:
103
this_branch = control_dir.open_branch(
104
possible_transports=possible_transports)
105
# This may be a heavy checkout, where we want the master branch
106
master_location = this_branch.get_bound_location()
107
if master_location is not None:
108
return master_location
109
# If not, use a local sibling
110
return this_branch.base
113
def _is_colocated(control_dir, possible_transports=None):
114
"""Check if the branch in control_dir is colocated.
116
:param control_dir: Control directory
117
:return: Tuple with boolean indicating whether the branch is colocated
118
and the full URL to the actual branch
120
# This path is meant to be relative to the existing branch
121
this_url = _get_branch_location(control_dir,
122
possible_transports=possible_transports)
123
# Perhaps the target control dir supports colocated branches?
125
root = controldir.ControlDir.open(this_url,
126
possible_transports=possible_transports)
127
except errors.NotBranchError:
128
return (False, this_url)
131
wt = control_dir.open_workingtree()
132
except (errors.NoWorkingTree, errors.NotLocalUrl):
133
return (False, this_url)
136
root._format.colocated_branches and
137
control_dir.control_url == root.control_url,
141
def lookup_new_sibling_branch(control_dir, location, possible_transports=None):
142
"""Lookup the location for a new sibling branch.
144
:param control_dir: Control directory to find sibling branches from
145
:param location: Name of the new branch
146
:return: Full location to the new branch
148
location = directory_service.directories.dereference(location)
149
if '/' not in location and '\\' not in location:
150
(colocated, this_url) = _is_colocated(control_dir, possible_transports)
153
return urlutils.join_segment_parameters(this_url,
154
{"branch": urlutils.escape(location)})
156
return urlutils.join(this_url, '..', urlutils.escape(location))
160
def open_sibling_branch(control_dir, location, possible_transports=None):
161
"""Open a branch, possibly a sibling of another.
163
:param control_dir: Control directory relative to which to lookup the
165
:param location: Location to look up
166
:return: branch to open
169
# Perhaps it's a colocated branch?
170
return control_dir.open_branch(location,
171
possible_transports=possible_transports)
172
except (errors.NotBranchError, errors.NoColocatedBranchSupport):
173
this_url = _get_branch_location(control_dir)
176
this_url, '..', urlutils.escape(location)))
179
def open_nearby_branch(near=None, location=None, possible_transports=None):
180
"""Open a nearby branch.
182
:param near: Optional location of container from which to open branch
183
:param location: Location of the branch
184
:return: Branch instance
190
return Branch.open(location,
191
possible_transports=possible_transports)
192
except errors.NotBranchError:
194
cdir = controldir.ControlDir.open(near,
195
possible_transports=possible_transports)
196
return open_sibling_branch(cdir, location,
197
possible_transports=possible_transports)
200
def iter_sibling_branches(control_dir, possible_transports=None):
201
"""Iterate over the siblings of a branch.
203
:param control_dir: Control directory for which to look up the siblings
204
:return: Iterator over tuples with branch name and branch object
208
reference = control_dir.get_branch_reference()
209
except errors.NotBranchError:
210
# There is no active branch, just return the colocated branches.
211
for name, branch in viewitems(control_dir.get_branches()):
214
if reference is not None:
215
ref_branch = Branch.open(reference,
216
possible_transports=possible_transports)
219
if ref_branch is None or ref_branch.name:
220
if ref_branch is not None:
221
control_dir = ref_branch.controldir
222
for name, branch in viewitems(control_dir.get_branches()):
225
repo = ref_branch.controldir.find_repository()
226
for branch in repo.find_branches(using=True):
227
name = urlutils.relative_url(repo.user_url,
228
branch.user_url).rstrip("/")
232
def tree_files_for_add(file_list):
234
Return a tree and list of absolute paths from a file list.
236
Similar to tree_files, but add handles files a bit differently, so it a
237
custom implementation. In particular, MutableTreeTree.smart_add expects
238
absolute paths, which it immediately converts to relative paths.
240
# FIXME Would be nice to just return the relative paths like
241
# internal_tree_files does, but there are a large number of unit tests
242
# that assume the current interface to mutabletree.smart_add
244
tree, relpath = WorkingTree.open_containing(file_list[0])
245
if tree.supports_views():
246
view_files = tree.views.lookup_view()
248
for filename in file_list:
249
if not osutils.is_inside_any(view_files, filename):
250
raise views.FileOutsideView(filename, view_files)
251
file_list = file_list[:]
252
file_list[0] = tree.abspath(relpath)
254
tree = WorkingTree.open_containing(u'.')[0]
255
if tree.supports_views():
256
view_files = tree.views.lookup_view()
258
file_list = view_files
259
view_str = views.view_display_str(view_files)
260
note(gettext("Ignoring files outside view. View is %s") % view_str)
261
return tree, file_list
264
def _get_one_revision(command_name, revisions):
265
if revisions is None:
267
if len(revisions) != 1:
268
raise errors.BzrCommandError(gettext(
269
'brz %s --revision takes exactly one revision identifier') % (
274
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
275
"""Get a revision tree. Not suitable for commands that change the tree.
277
Specifically, the basis tree in dirstate trees is coupled to the dirstate
278
and doing a commit/uncommit/pull will at best fail due to changing the
281
If tree is passed in, it should be already locked, for lifetime management
282
of the trees internal cached state.
286
if revisions is None:
288
rev_tree = tree.basis_tree()
290
rev_tree = branch.basis_tree()
292
revision = _get_one_revision(command_name, revisions)
293
rev_tree = revision.as_tree(branch)
297
def _get_view_info_for_change_reporter(tree):
298
"""Get the view information from a tree for change reporting."""
301
current_view = tree.views.get_view_info()[0]
302
if current_view is not None:
303
view_info = (current_view, tree.views.lookup_view())
304
except views.ViewsNotSupported:
309
def _open_directory_or_containing_tree_or_branch(filename, directory):
310
"""Open the tree or branch containing the specified file, unless
311
the --directory option is used to specify a different branch."""
312
if directory is not None:
313
return (None, Branch.open(directory), filename)
314
return controldir.ControlDir.open_containing_tree_or_branch(filename)
317
# TODO: Make sure no commands unconditionally use the working directory as a
318
# branch. If a filename argument is used, the first of them should be used to
319
# specify the branch. (Perhaps this can be factored out into some kind of
320
# Argument class, representing a file in a branch, where the first occurrence
323
class cmd_status(Command):
324
__doc__ = """Display status summary.
326
This reports on versioned and unknown files, reporting them
327
grouped by state. Possible states are:
330
Versioned in the working copy but not in the previous revision.
333
Versioned in the previous revision but removed or deleted
337
Path of this file changed from the previous revision;
338
the text may also have changed. This includes files whose
339
parent directory was renamed.
342
Text has changed since the previous revision.
345
File kind has been changed (e.g. from file to directory).
348
Not versioned and not matching an ignore pattern.
350
Additionally for directories, symlinks and files with a changed
351
executable bit, Bazaar indicates their type using a trailing
352
character: '/', '@' or '*' respectively. These decorations can be
353
disabled using the '--no-classify' option.
355
To see ignored files use 'brz ignored'. For details on the
356
changes to file texts, use 'brz diff'.
358
Note that --short or -S gives status flags for each item, similar
359
to Subversion's status command. To get output similar to svn -q,
362
If no arguments are specified, the status of the entire working
363
directory is shown. Otherwise, only the status of the specified
364
files or directories is reported. If a directory is given, status
365
is reported for everything inside that directory.
367
Before merges are committed, the pending merge tip revisions are
368
shown. To see all pending merge revisions, use the -v option.
369
To skip the display of pending merge information altogether, use
370
the no-pending option or specify a file/directory.
372
To compare the working directory to a specific revision, pass a
373
single revision to the revision argument.
375
To see which files have changed in a specific revision, or between
376
two revisions, pass a revision range to the revision argument.
377
This will produce the same results as calling 'brz diff --summarize'.
380
# TODO: --no-recurse/-N, --recurse options
382
takes_args = ['file*']
383
takes_options = ['show-ids', 'revision', 'change', 'verbose',
384
Option('short', help='Use short status indicators.',
386
Option('versioned', help='Only show versioned files.',
388
Option('no-pending', help='Don\'t show pending merges.',
390
Option('no-classify',
391
help='Do not mark object type using indicator.',
394
aliases = ['st', 'stat']
396
encoding_type = 'replace'
397
_see_also = ['diff', 'revert', 'status-flags']
400
def run(self, show_ids=False, file_list=None, revision=None, short=False,
401
versioned=False, no_pending=False, verbose=False,
403
from .status import show_tree_status
405
if revision and len(revision) > 2:
406
raise errors.BzrCommandError(gettext('brz status --revision takes exactly'
407
' one or two revision specifiers'))
409
tree, relfile_list = WorkingTree.open_containing_paths(file_list)
410
# Avoid asking for specific files when that is not needed.
411
if relfile_list == ['']:
413
# Don't disable pending merges for full trees other than '.'.
414
if file_list == ['.']:
416
# A specific path within a tree was given.
417
elif relfile_list is not None:
419
show_tree_status(tree, show_ids=show_ids,
420
specific_files=relfile_list, revision=revision,
421
to_file=self.outf, short=short, versioned=versioned,
422
show_pending=(not no_pending), verbose=verbose,
423
classify=not no_classify)
426
class cmd_cat_revision(Command):
427
__doc__ = """Write out metadata for a revision.
429
The revision to print can either be specified by a specific
430
revision identifier, or you can use --revision.
434
takes_args = ['revision_id?']
435
takes_options = ['directory', 'revision']
436
# cat-revision is more for frontends so should be exact
439
def print_revision(self, revisions, revid):
440
stream = revisions.get_record_stream([(revid,)], 'unordered', True)
441
record = next(stream)
442
if record.storage_kind == 'absent':
443
raise errors.NoSuchRevision(revisions, revid)
444
revtext = record.get_bytes_as('fulltext')
445
self.outf.write(revtext.decode('utf-8'))
448
def run(self, revision_id=None, revision=None, directory=u'.'):
449
if revision_id is not None and revision is not None:
450
raise errors.BzrCommandError(gettext('You can only supply one of'
451
' revision_id or --revision'))
452
if revision_id is None and revision is None:
453
raise errors.BzrCommandError(gettext('You must supply either'
454
' --revision or a revision_id'))
456
b = controldir.ControlDir.open_containing_tree_or_branch(directory)[1]
458
revisions = b.repository.revisions
459
if revisions is None:
460
raise errors.BzrCommandError(gettext('Repository %r does not support '
461
'access to raw revision texts'))
463
with b.repository.lock_read():
464
# TODO: jam 20060112 should cat-revision always output utf-8?
465
if revision_id is not None:
466
revision_id = cache_utf8.encode(revision_id)
468
self.print_revision(revisions, revision_id)
469
except errors.NoSuchRevision:
470
msg = gettext("The repository {0} contains no revision {1}.").format(
471
b.repository.base, revision_id.decode('utf-8'))
472
raise errors.BzrCommandError(msg)
473
elif revision is not None:
476
raise errors.BzrCommandError(
477
gettext('You cannot specify a NULL revision.'))
478
rev_id = rev.as_revision_id(b)
479
self.print_revision(revisions, rev_id)
482
class cmd_remove_tree(Command):
483
__doc__ = """Remove the working tree from a given branch/checkout.
485
Since a lightweight checkout is little more than a working tree
486
this will refuse to run against one.
488
To re-create the working tree, use "brz checkout".
490
_see_also = ['checkout', 'working-trees']
491
takes_args = ['location*']
494
help='Remove the working tree even if it has '
495
'uncommitted or shelved changes.'),
498
def run(self, location_list, force=False):
499
if not location_list:
502
for location in location_list:
503
d = controldir.ControlDir.open(location)
506
working = d.open_workingtree()
507
except errors.NoWorkingTree:
508
raise errors.BzrCommandError(gettext("No working tree to remove"))
509
except errors.NotLocalUrl:
510
raise errors.BzrCommandError(gettext("You cannot remove the working tree"
511
" of a remote path"))
513
if (working.has_changes()):
514
raise errors.UncommittedChanges(working)
515
if working.get_shelf_manager().last_shelf() is not None:
516
raise errors.ShelvedChanges(working)
518
if working.user_url != working.branch.user_url:
519
raise errors.BzrCommandError(gettext("You cannot remove the working tree"
520
" from a lightweight checkout"))
522
d.destroy_workingtree()
525
class cmd_repair_workingtree(Command):
526
__doc__ = """Reset the working tree state file.
528
This is not meant to be used normally, but more as a way to recover from
529
filesystem corruption, etc. This rebuilds the working inventory back to a
530
'known good' state. Any new modifications (adding a file, renaming, etc)
531
will be lost, though modified files will still be detected as such.
533
Most users will want something more like "brz revert" or "brz update"
534
unless the state file has become corrupted.
536
By default this attempts to recover the current state by looking at the
537
headers of the state file. If the state file is too corrupted to even do
538
that, you can supply --revision to force the state of the tree.
541
takes_options = ['revision', 'directory',
543
help='Reset the tree even if it doesn\'t appear to be'
548
def run(self, revision=None, directory='.', force=False):
549
tree, _ = WorkingTree.open_containing(directory)
550
self.add_cleanup(tree.lock_tree_write().unlock)
554
except errors.BzrError:
555
pass # There seems to be a real error here, so we'll reset
558
raise errors.BzrCommandError(gettext(
559
'The tree does not appear to be corrupt. You probably'
560
' want "brz revert" instead. Use "--force" if you are'
561
' sure you want to reset the working tree.'))
565
revision_ids = [r.as_revision_id(tree.branch) for r in revision]
567
tree.reset_state(revision_ids)
568
except errors.BzrError as e:
569
if revision_ids is None:
570
extra = (gettext(', the header appears corrupt, try passing -r -1'
571
' to set the state to the last commit'))
574
raise errors.BzrCommandError(gettext('failed to reset the tree state{0}').format(extra))
577
class cmd_revno(Command):
578
__doc__ = """Show current revision number.
580
This is equal to the number of revisions on this branch.
584
takes_args = ['location?']
586
Option('tree', help='Show revno of working tree.'),
591
def run(self, tree=False, location=u'.', revision=None):
592
if revision is not None and tree:
593
raise errors.BzrCommandError(gettext("--tree and --revision can "
594
"not be used together"))
598
wt = WorkingTree.open_containing(location)[0]
599
self.add_cleanup(wt.lock_read().unlock)
600
except (errors.NoWorkingTree, errors.NotLocalUrl):
601
raise errors.NoWorkingTree(location)
603
revid = wt.last_revision()
605
b = Branch.open_containing(location)[0]
606
self.add_cleanup(b.lock_read().unlock)
608
if len(revision) != 1:
609
raise errors.BzrCommandError(gettext(
610
"Revision numbers only make sense for single "
611
"revisions, not ranges"))
612
revid = revision[0].as_revision_id(b)
614
revid = b.last_revision()
616
revno_t = b.revision_id_to_dotted_revno(revid)
617
except errors.NoSuchRevision:
619
revno = ".".join(str(n) for n in revno_t)
621
self.outf.write(revno + '\n')
624
class cmd_revision_info(Command):
625
__doc__ = """Show revision number and revision id for a given revision identifier.
628
takes_args = ['revision_info*']
631
custom_help('directory',
632
help='Branch to examine, '
633
'rather than the one containing the working directory.'),
634
Option('tree', help='Show revno of working tree.'),
638
def run(self, revision=None, directory=u'.', tree=False,
639
revision_info_list=[]):
642
wt = WorkingTree.open_containing(directory)[0]
644
self.add_cleanup(wt.lock_read().unlock)
645
except (errors.NoWorkingTree, errors.NotLocalUrl):
647
b = Branch.open_containing(directory)[0]
648
self.add_cleanup(b.lock_read().unlock)
650
if revision is not None:
651
revision_ids.extend(rev.as_revision_id(b) for rev in revision)
652
if revision_info_list is not None:
653
for rev_str in revision_info_list:
654
rev_spec = RevisionSpec.from_string(rev_str)
655
revision_ids.append(rev_spec.as_revision_id(b))
656
# No arguments supplied, default to the last revision
657
if len(revision_ids) == 0:
660
raise errors.NoWorkingTree(directory)
661
revision_ids.append(wt.last_revision())
663
revision_ids.append(b.last_revision())
667
for revision_id in revision_ids:
669
dotted_revno = b.revision_id_to_dotted_revno(revision_id)
670
revno = '.'.join(str(i) for i in dotted_revno)
671
except errors.NoSuchRevision:
673
maxlen = max(maxlen, len(revno))
674
revinfos.append((revno, revision_id))
677
for revno, revid in revinfos:
678
self.outf.write('%*s %s\n' % (maxlen, revno, revid.decode('utf-8')))
681
class cmd_add(Command):
682
__doc__ = """Add specified files or directories.
684
In non-recursive mode, all the named items are added, regardless
685
of whether they were previously ignored. A warning is given if
686
any of the named files are already versioned.
688
In recursive mode (the default), files are treated the same way
689
but the behaviour for directories is different. Directories that
690
are already versioned do not give a warning. All directories,
691
whether already versioned or not, are searched for files or
692
subdirectories that are neither versioned or ignored, and these
693
are added. This search proceeds recursively into versioned
694
directories. If no names are given '.' is assumed.
696
A warning will be printed when nested trees are encountered,
697
unless they are explicitly ignored.
699
Therefore simply saying 'brz add' will version all files that
700
are currently unknown.
702
Adding a file whose parent directory is not versioned will
703
implicitly add the parent, and so on up to the root. This means
704
you should never need to explicitly add a directory, they'll just
705
get added when you add a file in the directory.
707
--dry-run will show which files would be added, but not actually
710
--file-ids-from will try to use the file ids from the supplied path.
711
It looks up ids trying to find a matching parent directory with the
712
same filename, and then by pure path. This option is rarely needed
713
but can be useful when adding the same logical file into two
714
branches that will be merged later (without showing the two different
715
adds as a conflict). It is also useful when merging another project
716
into a subdirectory of this one.
718
Any files matching patterns in the ignore list will not be added
719
unless they are explicitly mentioned.
721
In recursive mode, files larger than the configuration option
722
add.maximum_file_size will be skipped. Named items are never skipped due
725
takes_args = ['file*']
728
help="Don't recursively add the contents of directories.",
731
help="Show what would be done, but don't actually do anything."),
733
Option('file-ids-from',
735
help='Lookup file ids from this tree.'),
737
encoding_type = 'replace'
738
_see_also = ['remove', 'ignore']
740
def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
745
if file_ids_from is not None:
747
base_tree, base_path = WorkingTree.open_containing(
749
except errors.NoWorkingTree:
750
base_branch, base_path = Branch.open_containing(
752
base_tree = base_branch.basis_tree()
754
action = breezy.add.AddFromBaseAction(base_tree, base_path,
755
to_file=self.outf, should_print=(not is_quiet()))
757
action = breezy.add.AddWithSkipLargeAction(to_file=self.outf,
758
should_print=(not is_quiet()))
761
self.add_cleanup(base_tree.lock_read().unlock)
762
tree, file_list = tree_files_for_add(file_list)
763
added, ignored = tree.smart_add(file_list, not
764
no_recurse, action=action, save=not dry_run)
768
for glob in sorted(ignored):
769
for path in ignored[glob]:
771
gettext("ignored {0} matching \"{1}\"\n").format(
775
class cmd_mkdir(Command):
776
__doc__ = """Create a new versioned directory.
778
This is equivalent to creating the directory and then adding it.
781
takes_args = ['dir+']
785
help='No error if existing, make parent directories as needed.',
789
encoding_type = 'replace'
792
def add_file_with_parents(cls, wt, relpath):
793
if wt.is_versioned(relpath):
795
cls.add_file_with_parents(wt, osutils.dirname(relpath))
799
def add_file_single(cls, wt, relpath):
802
def run(self, dir_list, parents=False):
804
add_file = self.add_file_with_parents
806
add_file = self.add_file_single
808
wt, relpath = WorkingTree.open_containing(dir)
813
if e.errno != errno.EEXIST:
817
add_file(wt, relpath)
819
self.outf.write(gettext('added %s\n') % dir)
822
class cmd_relpath(Command):
823
__doc__ = """Show path of a file relative to root"""
825
takes_args = ['filename']
829
def run(self, filename):
830
# TODO: jam 20050106 Can relpath return a munged path if
831
# sys.stdout encoding cannot represent it?
832
tree, relpath = WorkingTree.open_containing(filename)
833
self.outf.write(relpath)
834
self.outf.write('\n')
837
class cmd_inventory(Command):
838
__doc__ = """Show inventory of the current working copy or a revision.
840
It is possible to limit the output to a particular entry
841
type using the --kind option. For example: --kind file.
843
It is also possible to restrict the list of files to a specific
844
set. For example: brz inventory --show-ids this/file
853
help='List entries of a particular kind: file, directory, symlink.',
856
takes_args = ['file*']
859
def run(self, revision=None, show_ids=False, kind=None, file_list=None):
860
if kind and kind not in ['file', 'directory', 'symlink']:
861
raise errors.BzrCommandError(gettext('invalid kind %r specified') % (kind,))
863
revision = _get_one_revision('inventory', revision)
864
work_tree, file_list = WorkingTree.open_containing_paths(file_list)
865
self.add_cleanup(work_tree.lock_read().unlock)
866
if revision is not None:
867
tree = revision.as_tree(work_tree.branch)
869
extra_trees = [work_tree]
870
self.add_cleanup(tree.lock_read().unlock)
875
self.add_cleanup(tree.lock_read().unlock)
876
if file_list is not None:
877
paths = tree.find_related_paths_across_trees(
878
file_list, extra_trees, require_versioned=True)
879
# find_ids_across_trees may include some paths that don't
881
entries = tree.iter_entries_by_dir(specific_files=paths)
883
entries = tree.iter_entries_by_dir()
885
for path, entry in sorted(entries):
886
if kind and kind != entry.kind:
891
self.outf.write('%-50s %s\n' % (path, entry.file_id.decode('utf-8')))
893
self.outf.write(path)
894
self.outf.write('\n')
897
class cmd_cp(Command):
898
__doc__ = """Copy a file.
901
brz cp OLDNAME NEWNAME
903
brz cp SOURCE... DESTINATION
905
If the last argument is a versioned directory, all the other names
906
are copied into it. Otherwise, there must be exactly two arguments
907
and the file is copied to a new name.
909
Files cannot be copied between branches. Only files can be copied
913
takes_args = ['names*']
916
encoding_type = 'replace'
918
def run(self, names_list):
920
if names_list is None:
922
if len(names_list) < 2:
923
raise errors.BzrCommandError(gettext("missing file argument"))
924
tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
925
for file_name in rel_names[0:-1]:
927
raise errors.BzrCommandError(gettext("can not copy root of branch"))
928
self.add_cleanup(tree.lock_tree_write().unlock)
929
into_existing = osutils.isdir(names_list[-1])
930
if not into_existing:
932
(src, dst) = rel_names
934
raise errors.BzrCommandError(gettext('to copy multiple files the'
935
' destination must be a versioned'
939
pairs = [(n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
940
for n in rel_names[:-1]]
942
for src, dst in pairs:
944
src_kind = tree.stored_kind(src)
945
except errors.NoSuchFile:
946
raise errors.BzrCommandError(
947
gettext('Could not copy %s => %s: %s is not versioned.')
950
raise errors.BzrCommandError(
951
gettext('Could not copy %s => %s . %s is not versioned\\.'
953
if src_kind == 'directory':
954
raise errors.BzrCommandError(
955
gettext('Could not copy %s => %s . %s is a directory.'
957
dst_parent = osutils.split(dst)[0]
960
dst_parent_kind = tree.stored_kind(dst_parent)
961
except errors.NoSuchFile:
962
raise errors.BzrCommandError(
963
gettext('Could not copy %s => %s: %s is not versioned.')
964
% (src, dst, dst_parent))
965
if dst_parent_kind != 'directory':
966
raise errors.BzrCommandError(
967
gettext('Could not copy to %s: %s is not a directory.')
968
% (dst_parent, dst_parent))
970
tree.copy_one(src, dst)
973
class cmd_mv(Command):
974
__doc__ = """Move or rename a file.
977
brz mv OLDNAME NEWNAME
979
brz mv SOURCE... DESTINATION
981
If the last argument is a versioned directory, all the other names
982
are moved into it. Otherwise, there must be exactly two arguments
983
and the file is changed to a new name.
985
If OLDNAME does not exist on the filesystem but is versioned and
986
NEWNAME does exist on the filesystem but is not versioned, mv
987
assumes that the file has been manually moved and only updates
988
its internal inventory to reflect that change.
989
The same is valid when moving many SOURCE files to a DESTINATION.
991
Files cannot be moved between branches.
994
takes_args = ['names*']
995
takes_options = [Option("after", help="Move only the brz identifier"
996
" of the file, because the file has already been moved."),
997
Option('auto', help='Automatically guess renames.'),
998
Option('dry-run', help='Avoid making changes when guessing renames.'),
1000
aliases = ['move', 'rename']
1001
encoding_type = 'replace'
1003
def run(self, names_list, after=False, auto=False, dry_run=False):
1005
return self.run_auto(names_list, after, dry_run)
1007
raise errors.BzrCommandError(gettext('--dry-run requires --auto.'))
1008
if names_list is None:
1010
if len(names_list) < 2:
1011
raise errors.BzrCommandError(gettext("missing file argument"))
1012
tree, rel_names = WorkingTree.open_containing_paths(names_list, canonicalize=False)
1013
for file_name in rel_names[0:-1]:
1015
raise errors.BzrCommandError(gettext("can not move root of branch"))
1016
self.add_cleanup(tree.lock_tree_write().unlock)
1017
self._run(tree, names_list, rel_names, after)
1019
def run_auto(self, names_list, after, dry_run):
1020
if names_list is not None and len(names_list) > 1:
1021
raise errors.BzrCommandError(gettext('Only one path may be specified to'
1024
raise errors.BzrCommandError(gettext('--after cannot be specified with'
1026
work_tree, file_list = WorkingTree.open_containing_paths(
1027
names_list, default_directory='.')
1028
self.add_cleanup(work_tree.lock_tree_write().unlock)
1029
rename_map.RenameMap.guess_renames(
1030
work_tree.basis_tree(), work_tree, dry_run)
1032
def _run(self, tree, names_list, rel_names, after):
1033
into_existing = osutils.isdir(names_list[-1])
1034
if into_existing and len(names_list) == 2:
1036
# a. case-insensitive filesystem and change case of dir
1037
# b. move directory after the fact (if the source used to be
1038
# a directory, but now doesn't exist in the working tree
1039
# and the target is an existing directory, just rename it)
1040
if (not tree.case_sensitive
1041
and rel_names[0].lower() == rel_names[1].lower()):
1042
into_existing = False
1044
# 'fix' the case of a potential 'from'
1045
from_path = tree.get_canonical_inventory_path(rel_names[0])
1046
if (not osutils.lexists(names_list[0]) and
1047
tree.is_versioned(from_path) and
1048
tree.stored_kind(from_path) == "directory"):
1049
into_existing = False
1052
# move into existing directory
1053
# All entries reference existing inventory items, so fix them up
1054
# for cicp file-systems.
1055
rel_names = tree.get_canonical_inventory_paths(rel_names)
1056
for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
1058
self.outf.write("%s => %s\n" % (src, dest))
1060
if len(names_list) != 2:
1061
raise errors.BzrCommandError(gettext('to mv multiple files the'
1062
' destination must be a versioned'
1065
# for cicp file-systems: the src references an existing inventory
1067
src = tree.get_canonical_inventory_path(rel_names[0])
1068
# Find the canonical version of the destination: In all cases, the
1069
# parent of the target must be in the inventory, so we fetch the
1070
# canonical version from there (we do not always *use* the
1071
# canonicalized tail portion - we may be attempting to rename the
1073
canon_dest = tree.get_canonical_inventory_path(rel_names[1])
1074
dest_parent = osutils.dirname(canon_dest)
1075
spec_tail = osutils.basename(rel_names[1])
1076
# For a CICP file-system, we need to avoid creating 2 inventory
1077
# entries that differ only by case. So regardless of the case
1078
# we *want* to use (ie, specified by the user or the file-system),
1079
# we must always choose to use the case of any existing inventory
1080
# items. The only exception to this is when we are attempting a
1081
# case-only rename (ie, canonical versions of src and dest are
1083
dest_id = tree.path2id(canon_dest)
1084
if dest_id is None or tree.path2id(src) == dest_id:
1085
# No existing item we care about, so work out what case we
1086
# are actually going to use.
1088
# If 'after' is specified, the tail must refer to a file on disk.
1090
dest_parent_fq = osutils.pathjoin(tree.basedir, dest_parent)
1092
# pathjoin with an empty tail adds a slash, which breaks
1094
dest_parent_fq = tree.basedir
1096
dest_tail = osutils.canonical_relpath(
1098
osutils.pathjoin(dest_parent_fq, spec_tail))
1100
# not 'after', so case as specified is used
1101
dest_tail = spec_tail
1103
# Use the existing item so 'mv' fails with AlreadyVersioned.
1104
dest_tail = os.path.basename(canon_dest)
1105
dest = osutils.pathjoin(dest_parent, dest_tail)
1106
mutter("attempting to move %s => %s", src, dest)
1107
tree.rename_one(src, dest, after=after)
1109
self.outf.write("%s => %s\n" % (src, dest))
1112
class cmd_pull(Command):
1113
__doc__ = """Turn this branch into a mirror of another branch.
1115
By default, this command only works on branches that have not diverged.
1116
Branches are considered diverged if the destination branch's most recent
1117
commit is one that has not been merged (directly or indirectly) into the
1120
If branches have diverged, you can use 'brz merge' to integrate the changes
1121
from one into the other. Once one branch has merged, the other should
1122
be able to pull it again.
1124
If you want to replace your local changes and just want your branch to
1125
match the remote one, use pull --overwrite. This will work even if the two
1126
branches have diverged.
1128
If there is no default location set, the first pull will set it (use
1129
--no-remember to avoid setting it). After that, you can omit the
1130
location to use the default. To change the default, use --remember. The
1131
value will only be saved if the remote location can be accessed.
1133
The --verbose option will display the revisions pulled using the log_format
1134
configuration option. You can use a different format by overriding it with
1135
-Olog_format=<other_format>.
1137
Note: The location can be specified either in the form of a branch,
1138
or in the form of a path to a file containing a merge directive generated
1142
_see_also = ['push', 'update', 'status-flags', 'send']
1143
takes_options = ['remember', 'overwrite', 'revision',
1144
custom_help('verbose',
1145
help='Show logs of pulled revisions.'),
1146
custom_help('directory',
1147
help='Branch to pull into, '
1148
'rather than the one containing the working directory.'),
1150
help="Perform a local pull in a bound "
1151
"branch. Local pulls are not applied to "
1152
"the master branch."
1155
help="Show base revision text in conflicts."),
1156
Option('overwrite-tags',
1157
help="Overwrite tags only."),
1159
takes_args = ['location?']
1160
encoding_type = 'replace'
1162
def run(self, location=None, remember=None, overwrite=False,
1163
revision=None, verbose=False,
1164
directory=None, local=False,
1165
show_base=False, overwrite_tags=False):
1168
overwrite = ["history", "tags"]
1169
elif overwrite_tags:
1170
overwrite = ["tags"]
1173
# FIXME: too much stuff is in the command class
1176
if directory is None:
1179
tree_to = WorkingTree.open_containing(directory)[0]
1180
branch_to = tree_to.branch
1181
self.add_cleanup(tree_to.lock_write().unlock)
1182
except errors.NoWorkingTree:
1184
branch_to = Branch.open_containing(directory)[0]
1185
self.add_cleanup(branch_to.lock_write().unlock)
1187
warning(gettext("No working tree, ignoring --show-base"))
1189
if local and not branch_to.get_bound_location():
1190
raise errors.LocalRequiresBoundBranch()
1192
possible_transports = []
1193
if location is not None:
1195
mergeable = bundle.read_mergeable_from_url(location,
1196
possible_transports=possible_transports)
1197
except errors.NotABundle:
1200
stored_loc = branch_to.get_parent()
1201
if location is None:
1202
if stored_loc is None:
1203
raise errors.BzrCommandError(gettext("No pull location known or"
1206
display_url = urlutils.unescape_for_display(stored_loc,
1209
self.outf.write(gettext("Using saved parent location: %s\n") % display_url)
1210
location = stored_loc
1212
revision = _get_one_revision('pull', revision)
1213
if mergeable is not None:
1214
if revision is not None:
1215
raise errors.BzrCommandError(gettext(
1216
'Cannot use -r with merge directives or bundles'))
1217
mergeable.install_revisions(branch_to.repository)
1218
base_revision_id, revision_id, verified = \
1219
mergeable.get_merge_request(branch_to.repository)
1220
branch_from = branch_to
1222
branch_from = Branch.open(location,
1223
possible_transports=possible_transports)
1224
self.add_cleanup(branch_from.lock_read().unlock)
1225
# Remembers if asked explicitly or no previous location is set
1227
or (remember is None and branch_to.get_parent() is None)):
1228
# FIXME: This shouldn't be done before the pull
1229
# succeeds... -- vila 2012-01-02
1230
branch_to.set_parent(branch_from.base)
1232
if revision is not None:
1233
revision_id = revision.as_revision_id(branch_from)
1235
if tree_to is not None:
1236
view_info = _get_view_info_for_change_reporter(tree_to)
1237
change_reporter = delta._ChangeReporter(
1238
unversioned_filter=tree_to.is_ignored,
1239
view_info=view_info)
1240
result = tree_to.pull(
1241
branch_from, overwrite, revision_id, change_reporter,
1242
local=local, show_base=show_base)
1244
result = branch_to.pull(
1245
branch_from, overwrite, revision_id, local=local)
1247
result.report(self.outf)
1248
if verbose and result.old_revid != result.new_revid:
1249
log.show_branch_change(
1250
branch_to, self.outf, result.old_revno,
1252
if getattr(result, 'tag_conflicts', None):
1258
class cmd_push(Command):
1259
__doc__ = """Update a mirror of this branch.
1261
The target branch will not have its working tree populated because this
1262
is both expensive, and is not supported on remote file systems.
1264
Some smart servers or protocols *may* put the working tree in place in
1267
This command only works on branches that have not diverged. Branches are
1268
considered diverged if the destination branch's most recent commit is one
1269
that has not been merged (directly or indirectly) by the source branch.
1271
If branches have diverged, you can use 'brz push --overwrite' to replace
1272
the other branch completely, discarding its unmerged changes.
1274
If you want to ensure you have the different changes in the other branch,
1275
do a merge (see brz help merge) from the other branch, and commit that.
1276
After that you will be able to do a push without '--overwrite'.
1278
If there is no default push location set, the first push will set it (use
1279
--no-remember to avoid setting it). After that, you can omit the
1280
location to use the default. To change the default, use --remember. The
1281
value will only be saved if the remote location can be accessed.
1283
The --verbose option will display the revisions pushed using the log_format
1284
configuration option. You can use a different format by overriding it with
1285
-Olog_format=<other_format>.
1288
_see_also = ['pull', 'update', 'working-trees']
1289
takes_options = ['remember', 'overwrite', 'verbose', 'revision',
1290
Option('create-prefix',
1291
help='Create the path leading up to the branch '
1292
'if it does not already exist.'),
1293
custom_help('directory',
1294
help='Branch to push from, '
1295
'rather than the one containing the working directory.'),
1296
Option('use-existing-dir',
1297
help='By default push will fail if the target'
1298
' directory exists, but does not already'
1299
' have a control directory. This flag will'
1300
' allow push to proceed.'),
1302
help='Create a stacked branch that references the public location '
1303
'of the parent branch.'),
1304
Option('stacked-on',
1305
help='Create a stacked branch that refers to another branch '
1306
'for the commit history. Only the work not present in the '
1307
'referenced branch is included in the branch created.',
1310
help='Refuse to push if there are uncommitted changes in'
1311
' the working tree, --no-strict disables the check.'),
1313
help="Don't populate the working tree, even for protocols"
1314
" that support it."),
1315
Option('overwrite-tags',
1316
help="Overwrite tags only."),
1317
Option('lossy', help="Allow lossy push, i.e. dropping metadata "
1318
"that can't be represented in the target.")
1320
takes_args = ['location?']
1321
encoding_type = 'replace'
1323
def run(self, location=None, remember=None, overwrite=False,
1324
create_prefix=False, verbose=False, revision=None,
1325
use_existing_dir=False, directory=None, stacked_on=None,
1326
stacked=False, strict=None, no_tree=False,
1327
overwrite_tags=False, lossy=False):
1328
from .push import _show_push_branch
1331
overwrite = ["history", "tags"]
1332
elif overwrite_tags:
1333
overwrite = ["tags"]
1337
if directory is None:
1339
# Get the source branch
1341
_unused) = controldir.ControlDir.open_containing_tree_or_branch(directory)
1342
# Get the tip's revision_id
1343
revision = _get_one_revision('push', revision)
1344
if revision is not None:
1345
revision_id = revision.in_history(br_from).rev_id
1348
if tree is not None and revision_id is None:
1349
tree.check_changed_or_out_of_date(
1350
strict, 'push_strict',
1351
more_error='Use --no-strict to force the push.',
1352
more_warning='Uncommitted changes will not be pushed.')
1353
# Get the stacked_on branch, if any
1354
if stacked_on is not None:
1355
stacked_on = urlutils.normalize_url(stacked_on)
1357
parent_url = br_from.get_parent()
1359
parent = Branch.open(parent_url)
1360
stacked_on = parent.get_public_branch()
1362
# I considered excluding non-http url's here, thus forcing
1363
# 'public' branches only, but that only works for some
1364
# users, so it's best to just depend on the user spotting an
1365
# error by the feedback given to them. RBC 20080227.
1366
stacked_on = parent_url
1368
raise errors.BzrCommandError(gettext(
1369
"Could not determine branch to refer to."))
1371
# Get the destination location
1372
if location is None:
1373
stored_loc = br_from.get_push_location()
1374
if stored_loc is None:
1375
parent_loc = br_from.get_parent()
1377
raise errors.BzrCommandError(gettext(
1378
"No push location known or specified. To push to the "
1379
"parent branch (at %s), use 'brz push :parent'." %
1380
urlutils.unescape_for_display(parent_loc,
1381
self.outf.encoding)))
1383
raise errors.BzrCommandError(gettext(
1384
"No push location known or specified."))
1386
display_url = urlutils.unescape_for_display(stored_loc,
1388
note(gettext("Using saved push location: %s") % display_url)
1389
location = stored_loc
1391
_show_push_branch(br_from, revision_id, location, self.outf,
1392
verbose=verbose, overwrite=overwrite, remember=remember,
1393
stacked_on=stacked_on, create_prefix=create_prefix,
1394
use_existing_dir=use_existing_dir, no_tree=no_tree,
1398
class cmd_branch(Command):
1399
__doc__ = """Create a new branch that is a copy of an existing branch.
1401
If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
1402
be used. In other words, "branch ../foo/bar" will attempt to create ./bar.
1403
If the FROM_LOCATION has no / or path separator embedded, the TO_LOCATION
1404
is derived from the FROM_LOCATION by stripping a leading scheme or drive
1405
identifier, if any. For example, "branch lp:foo-bar" will attempt to
1408
To retrieve the branch as of a particular revision, supply the --revision
1409
parameter, as in "branch foo/bar -r 5".
1412
_see_also = ['checkout']
1413
takes_args = ['from_location', 'to_location?']
1414
takes_options = ['revision',
1415
Option('hardlink', help='Hard-link working tree files where possible.'),
1416
Option('files-from', type=text_type,
1417
help="Get file contents from this tree."),
1419
help="Create a branch without a working-tree."),
1421
help="Switch the checkout in the current directory "
1422
"to the new branch."),
1424
help='Create a stacked branch referring to the source branch. '
1425
'The new branch will depend on the availability of the source '
1426
'branch for all operations.'),
1427
Option('standalone',
1428
help='Do not use a shared repository, even if available.'),
1429
Option('use-existing-dir',
1430
help='By default branch will fail if the target'
1431
' directory exists, but does not already'
1432
' have a control directory. This flag will'
1433
' allow branch to proceed.'),
1435
help="Bind new branch to from location."),
1438
def run(self, from_location, to_location=None, revision=None,
1439
hardlink=False, stacked=False, standalone=False, no_tree=False,
1440
use_existing_dir=False, switch=False, bind=False,
1442
from breezy import switch as _mod_switch
1443
accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
1445
if not (hardlink or files_from):
1446
# accelerator_tree is usually slower because you have to read N
1447
# files (no readahead, lots of seeks, etc), but allow the user to
1448
# explicitly request it
1449
accelerator_tree = None
1450
if files_from is not None and files_from != from_location:
1451
accelerator_tree = WorkingTree.open(files_from)
1452
revision = _get_one_revision('branch', revision)
1453
self.add_cleanup(br_from.lock_read().unlock)
1454
if revision is not None:
1455
revision_id = revision.as_revision_id(br_from)
1457
# FIXME - wt.last_revision, fallback to branch, fall back to
1458
# None or perhaps NULL_REVISION to mean copy nothing
1460
revision_id = br_from.last_revision()
1461
if to_location is None:
1462
to_location = getattr(br_from, "name", None)
1464
to_location = urlutils.derive_to_location(from_location)
1465
to_transport = transport.get_transport(to_location)
1467
to_transport.mkdir('.')
1468
except errors.FileExists:
1470
to_dir = controldir.ControlDir.open_from_transport(
1472
except errors.NotBranchError:
1473
if not use_existing_dir:
1474
raise errors.BzrCommandError(gettext('Target directory "%s" '
1475
'already exists.') % to_location)
1480
to_dir.open_branch()
1481
except errors.NotBranchError:
1484
raise errors.AlreadyBranchError(to_location)
1485
except errors.NoSuchFile:
1486
raise errors.BzrCommandError(gettext('Parent of "%s" does not exist.')
1492
# preserve whatever source format we have.
1493
to_dir = br_from.controldir.sprout(
1494
to_transport.base, revision_id,
1495
possible_transports=[to_transport],
1496
accelerator_tree=accelerator_tree, hardlink=hardlink,
1497
stacked=stacked, force_new_repo=standalone,
1498
create_tree_if_local=not no_tree, source_branch=br_from)
1499
branch = to_dir.open_branch(
1500
possible_transports=[
1501
br_from.controldir.root_transport, to_transport])
1502
except errors.NoSuchRevision:
1503
to_transport.delete_tree('.')
1504
msg = gettext("The branch {0} has no revision {1}.").format(
1505
from_location, revision)
1506
raise errors.BzrCommandError(msg)
1509
to_repo = to_dir.open_repository()
1510
except errors.NoRepositoryPresent:
1511
to_repo = to_dir.create_repository()
1512
to_repo.fetch(br_from.repository, revision_id=revision_id)
1513
branch = br_from.sprout(to_dir, revision_id=revision_id)
1514
br_from.tags.merge_to(branch.tags)
1516
# If the source branch is stacked, the new branch may
1517
# be stacked whether we asked for that explicitly or not.
1518
# We therefore need a try/except here and not just 'if stacked:'
1520
note(gettext('Created new stacked branch referring to %s.') %
1521
branch.get_stacked_on_url())
1522
except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
1523
errors.UnstackableRepositoryFormat) as e:
1524
note(ngettext('Branched %d revision.', 'Branched %d revisions.', branch.revno()) % branch.revno())
1526
# Bind to the parent
1527
parent_branch = Branch.open(from_location)
1528
branch.bind(parent_branch)
1529
note(gettext('New branch bound to %s') % from_location)
1531
# Switch to the new branch
1532
wt, _ = WorkingTree.open_containing('.')
1533
_mod_switch.switch(wt.controldir, branch)
1534
note(gettext('Switched to branch: %s'),
1535
urlutils.unescape_for_display(branch.base, 'utf-8'))
1538
class cmd_branches(Command):
1539
__doc__ = """List the branches available at the current location.
1541
This command will print the names of all the branches at the current
1545
takes_args = ['location?']
1547
Option('recursive', short_name='R',
1548
help='Recursively scan for branches rather than '
1549
'just looking in the specified location.')]
1551
def run(self, location=".", recursive=False):
1553
t = transport.get_transport(location)
1554
if not t.listable():
1555
raise errors.BzrCommandError(
1556
"Can't scan this type of location.")
1557
for b in controldir.ControlDir.find_branches(t):
1558
self.outf.write("%s\n" % urlutils.unescape_for_display(
1559
urlutils.relative_url(t.base, b.base),
1560
self.outf.encoding).rstrip("/"))
1562
dir = controldir.ControlDir.open_containing(location)[0]
1564
active_branch = dir.open_branch(name="")
1565
except errors.NotBranchError:
1566
active_branch = None
1568
for name, branch in iter_sibling_branches(dir):
1571
active = (active_branch is not None and
1572
active_branch.user_url == branch.user_url)
1573
names[name] = active
1574
# Only mention the current branch explicitly if it's not
1575
# one of the colocated branches
1576
if not any(viewvalues(names)) and active_branch is not None:
1577
self.outf.write("* %s\n" % gettext("(default)"))
1578
for name in sorted(names):
1579
active = names[name]
1584
self.outf.write("%s %s\n" % (
1585
prefix, (name if PY3 else name.encode(self.outf.encoding))))
1588
class cmd_checkout(Command):
1589
__doc__ = """Create a new checkout of an existing branch.
1591
If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree for
1592
the branch found in '.'. This is useful if you have removed the working tree
1593
or if it was never created - i.e. if you pushed the branch to its current
1594
location using SFTP.
1596
If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION will
1597
be used. In other words, "checkout ../foo/bar" will attempt to create ./bar.
1598
If the BRANCH_LOCATION has no / or path separator embedded, the TO_LOCATION
1599
is derived from the BRANCH_LOCATION by stripping a leading scheme or drive
1600
identifier, if any. For example, "checkout lp:foo-bar" will attempt to
1603
To retrieve the branch as of a particular revision, supply the --revision
1604
parameter, as in "checkout foo/bar -r 5". Note that this will be immediately
1605
out of date [so you cannot commit] but it may be useful (i.e. to examine old
1609
_see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
1610
takes_args = ['branch_location?', 'to_location?']
1611
takes_options = ['revision',
1612
Option('lightweight',
1613
help="Perform a lightweight checkout. Lightweight "
1614
"checkouts depend on access to the branch for "
1615
"every operation. Normal checkouts can perform "
1616
"common operations like diff and status without "
1617
"such access, and also support local commits."
1619
Option('files-from', type=text_type,
1620
help="Get file contents from this tree."),
1622
help='Hard-link working tree files where possible.'
1627
def run(self, branch_location=None, to_location=None, revision=None,
1628
lightweight=False, files_from=None, hardlink=False):
1629
if branch_location is None:
1630
branch_location = osutils.getcwd()
1631
to_location = branch_location
1632
accelerator_tree, source = controldir.ControlDir.open_tree_or_branch(
1634
if not (hardlink or files_from):
1635
# accelerator_tree is usually slower because you have to read N
1636
# files (no readahead, lots of seeks, etc), but allow the user to
1637
# explicitly request it
1638
accelerator_tree = None
1639
revision = _get_one_revision('checkout', revision)
1640
if files_from is not None and files_from != branch_location:
1641
accelerator_tree = WorkingTree.open(files_from)
1642
if revision is not None:
1643
revision_id = revision.as_revision_id(source)
1646
if to_location is None:
1647
to_location = urlutils.derive_to_location(branch_location)
1648
# if the source and to_location are the same,
1649
# and there is no working tree,
1650
# then reconstitute a branch
1651
if (osutils.abspath(to_location) ==
1652
osutils.abspath(branch_location)):
1654
source.controldir.open_workingtree()
1655
except errors.NoWorkingTree:
1656
source.controldir.create_workingtree(revision_id)
1658
source.create_checkout(to_location, revision_id, lightweight,
1659
accelerator_tree, hardlink)
1662
class cmd_renames(Command):
1663
__doc__ = """Show list of renamed files.
1665
# TODO: Option to show renames between two historical versions.
1667
# TODO: Only show renames under dir, rather than in the whole branch.
1668
_see_also = ['status']
1669
takes_args = ['dir?']
1672
def run(self, dir=u'.'):
1673
tree = WorkingTree.open_containing(dir)[0]
1674
self.add_cleanup(tree.lock_read().unlock)
1675
old_tree = tree.basis_tree()
1676
self.add_cleanup(old_tree.lock_read().unlock)
1678
iterator = tree.iter_changes(old_tree, include_unchanged=True)
1679
for f, paths, c, v, p, n, k, e in iterator:
1680
if paths[0] == paths[1]:
1684
renames.append(paths)
1686
for old_name, new_name in renames:
1687
self.outf.write("%s => %s\n" % (old_name, new_name))
1690
class cmd_update(Command):
1691
__doc__ = """Update a working tree to a new revision.
1693
This will perform a merge of the destination revision (the tip of the
1694
branch, or the specified revision) into the working tree, and then make
1695
that revision the basis revision for the working tree.
1697
You can use this to visit an older revision, or to update a working tree
1698
that is out of date from its branch.
1700
If there are any uncommitted changes in the tree, they will be carried
1701
across and remain as uncommitted changes after the update. To discard
1702
these changes, use 'brz revert'. The uncommitted changes may conflict
1703
with the changes brought in by the change in basis revision.
1705
If the tree's branch is bound to a master branch, brz will also update
1706
the branch from the master.
1708
You cannot update just a single file or directory, because each Bazaar
1709
working tree has just a single basis revision. If you want to restore a
1710
file that has been removed locally, use 'brz revert' instead of 'brz
1711
update'. If you want to restore a file to its state in a previous
1712
revision, use 'brz revert' with a '-r' option, or use 'brz cat' to write
1713
out the old content of that file to a new location.
1715
The 'dir' argument, if given, must be the location of the root of a
1716
working tree to update. By default, the working tree that contains the
1717
current working directory is used.
1720
_see_also = ['pull', 'working-trees', 'status-flags']
1721
takes_args = ['dir?']
1722
takes_options = ['revision',
1724
help="Show base revision text in conflicts."),
1728
def run(self, dir=None, revision=None, show_base=None):
1729
if revision is not None and len(revision) != 1:
1730
raise errors.BzrCommandError(gettext(
1731
"brz update --revision takes exactly one revision"))
1733
tree = WorkingTree.open_containing('.')[0]
1735
tree, relpath = WorkingTree.open_containing(dir)
1738
raise errors.BzrCommandError(gettext(
1739
"brz update can only update a whole tree, "
1740
"not a file or subdirectory"))
1741
branch = tree.branch
1742
possible_transports = []
1743
master = branch.get_master_branch(
1744
possible_transports=possible_transports)
1745
if master is not None:
1746
branch_location = master.base
1749
branch_location = tree.branch.base
1750
tree.lock_tree_write()
1751
self.add_cleanup(tree.unlock)
1752
# get rid of the final '/' and be ready for display
1753
branch_location = urlutils.unescape_for_display(
1754
branch_location.rstrip('/'),
1756
existing_pending_merges = tree.get_parent_ids()[1:]
1760
# may need to fetch data into a heavyweight checkout
1761
# XXX: this may take some time, maybe we should display a
1763
old_tip = branch.update(possible_transports)
1764
if revision is not None:
1765
revision_id = revision[0].as_revision_id(branch)
1767
revision_id = branch.last_revision()
1768
if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1769
revno = branch.revision_id_to_dotted_revno(revision_id)
1770
note(gettext("Tree is up to date at revision {0} of branch {1}"
1771
).format('.'.join(map(str, revno)), branch_location))
1773
view_info = _get_view_info_for_change_reporter(tree)
1774
change_reporter = delta._ChangeReporter(
1775
unversioned_filter=tree.is_ignored,
1776
view_info=view_info)
1778
conflicts = tree.update(
1780
possible_transports=possible_transports,
1781
revision=revision_id,
1783
show_base=show_base)
1784
except errors.NoSuchRevision as e:
1785
raise errors.BzrCommandError(gettext(
1786
"branch has no revision %s\n"
1787
"brz update --revision only works"
1788
" for a revision in the branch history")
1790
revno = tree.branch.revision_id_to_dotted_revno(
1791
_mod_revision.ensure_null(tree.last_revision()))
1792
note(gettext('Updated to revision {0} of branch {1}').format(
1793
'.'.join(map(str, revno)), branch_location))
1794
parent_ids = tree.get_parent_ids()
1795
if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
1796
note(gettext('Your local commits will now show as pending merges with '
1797
"'brz status', and can be committed with 'brz commit'."))
1804
class cmd_info(Command):
1805
__doc__ = """Show information about a working tree, branch or repository.
1807
This command will show all known locations and formats associated to the
1808
tree, branch or repository.
1810
In verbose mode, statistical information is included with each report.
1811
To see extended statistic information, use a verbosity level of 2 or
1812
higher by specifying the verbose option multiple times, e.g. -vv.
1814
Branches and working trees will also report any missing revisions.
1818
Display information on the format and related locations:
1822
Display the above together with extended format information and
1823
basic statistics (like the number of files in the working tree and
1824
number of revisions in the branch and repository):
1828
Display the above together with number of committers to the branch:
1832
_see_also = ['revno', 'working-trees', 'repositories']
1833
takes_args = ['location?']
1834
takes_options = ['verbose']
1835
encoding_type = 'replace'
1838
def run(self, location=None, verbose=False):
1840
noise_level = get_verbosity_level()
1843
from .info import show_bzrdir_info
1844
show_bzrdir_info(controldir.ControlDir.open_containing(location)[0],
1845
verbose=noise_level, outfile=self.outf)
1848
class cmd_remove(Command):
1849
__doc__ = """Remove files or directories.
1851
This makes Bazaar stop tracking changes to the specified files. Bazaar will
1852
delete them if they can easily be recovered using revert otherwise they
1853
will be backed up (adding an extension of the form .~#~). If no options or
1854
parameters are given Bazaar will scan for files that are being tracked by
1855
Bazaar but missing in your tree and stop tracking them for you.
1857
takes_args = ['file*']
1858
takes_options = ['verbose',
1859
Option('new', help='Only remove files that have never been committed.'),
1860
RegistryOption.from_kwargs('file-deletion-strategy',
1861
'The file deletion mode to be used.',
1862
title='Deletion Strategy', value_switches=True, enum_switch=False,
1863
safe='Backup changed files (default).',
1864
keep='Delete from brz but leave the working copy.',
1865
no_backup='Don\'t backup changed files.'),
1867
aliases = ['rm', 'del']
1868
encoding_type = 'replace'
1870
def run(self, file_list, verbose=False, new=False,
1871
file_deletion_strategy='safe'):
1873
tree, file_list = WorkingTree.open_containing_paths(file_list)
1875
if file_list is not None:
1876
file_list = [f for f in file_list]
1878
self.add_cleanup(tree.lock_write().unlock)
1879
# Heuristics should probably all move into tree.remove_smart or
1882
added = tree.changes_from(tree.basis_tree(),
1883
specific_files=file_list).added
1884
file_list = sorted([f[0] for f in added], reverse=True)
1885
if len(file_list) == 0:
1886
raise errors.BzrCommandError(gettext('No matching files.'))
1887
elif file_list is None:
1888
# missing files show up in iter_changes(basis) as
1889
# versioned-with-no-kind.
1891
for change in tree.iter_changes(tree.basis_tree()):
1892
# Find paths in the working tree that have no kind:
1893
if change[1][1] is not None and change[6][1] is None:
1894
missing.append(change[1][1])
1895
file_list = sorted(missing, reverse=True)
1896
file_deletion_strategy = 'keep'
1897
tree.remove(file_list, verbose=verbose, to_file=self.outf,
1898
keep_files=file_deletion_strategy=='keep',
1899
force=(file_deletion_strategy=='no-backup'))
1902
class cmd_file_id(Command):
1903
__doc__ = """Print file_id of a particular file or directory.
1905
The file_id is assigned when the file is first added and remains the
1906
same through all revisions where the file exists, even when it is
1911
_see_also = ['inventory', 'ls']
1912
takes_args = ['filename']
1915
def run(self, filename):
1916
tree, relpath = WorkingTree.open_containing(filename)
1917
file_id = tree.path2id(relpath)
1919
raise errors.NotVersionedError(filename)
1921
self.outf.write(file_id.decode('utf-8') + '\n')
1924
class cmd_file_path(Command):
1925
__doc__ = """Print path of file_ids to a file or directory.
1927
This prints one line for each directory down to the target,
1928
starting at the branch root.
1932
takes_args = ['filename']
1935
def run(self, filename):
1936
tree, relpath = WorkingTree.open_containing(filename)
1937
fid = tree.path2id(relpath)
1939
raise errors.NotVersionedError(filename)
1940
segments = osutils.splitpath(relpath)
1941
for pos in range(1, len(segments) + 1):
1942
path = osutils.joinpath(segments[:pos])
1943
self.outf.write("%s\n" % tree.path2id(path))
1946
class cmd_reconcile(Command):
1947
__doc__ = """Reconcile brz metadata in a branch.
1949
This can correct data mismatches that may have been caused by
1950
previous ghost operations or brz upgrades. You should only
1951
need to run this command if 'brz check' or a brz developer
1952
advises you to run it.
1954
If a second branch is provided, cross-branch reconciliation is
1955
also attempted, which will check that data like the tree root
1956
id which was not present in very early brz versions is represented
1957
correctly in both branches.
1959
At the same time it is run it may recompress data resulting in
1960
a potential saving in disk space or performance gain.
1962
The branch *MUST* be on a listable system such as local disk or sftp.
1965
_see_also = ['check']
1966
takes_args = ['branch?']
1968
Option('canonicalize-chks',
1969
help='Make sure CHKs are in canonical form (repairs '
1974
def run(self, branch=".", canonicalize_chks=False):
1975
from .reconcile import reconcile
1976
dir = controldir.ControlDir.open(branch)
1977
reconcile(dir, canonicalize_chks=canonicalize_chks)
1980
class cmd_revision_history(Command):
1981
__doc__ = """Display the list of revision ids on a branch."""
1984
takes_args = ['location?']
1989
def run(self, location="."):
1990
branch = Branch.open_containing(location)[0]
1991
self.add_cleanup(branch.lock_read().unlock)
1992
graph = branch.repository.get_graph()
1993
history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
1994
[_mod_revision.NULL_REVISION]))
1995
for revid in reversed(history):
1996
self.outf.write(revid)
1997
self.outf.write('\n')
2000
class cmd_ancestry(Command):
2001
__doc__ = """List all revisions merged into this branch."""
2003
_see_also = ['log', 'revision-history']
2004
takes_args = ['location?']
2009
def run(self, location="."):
2011
wt = WorkingTree.open_containing(location)[0]
2012
except errors.NoWorkingTree:
2013
b = Branch.open(location)
2014
last_revision = b.last_revision()
2017
last_revision = wt.last_revision()
2019
self.add_cleanup(b.repository.lock_read().unlock)
2020
graph = b.repository.get_graph()
2021
revisions = [revid for revid, parents in
2022
graph.iter_ancestry([last_revision])]
2023
for revision_id in reversed(revisions):
2024
if _mod_revision.is_null(revision_id):
2026
self.outf.write(revision_id.decode('utf-8') + '\n')
2029
class cmd_init(Command):
2030
__doc__ = """Make a directory into a versioned branch.
2032
Use this to create an empty branch, or before importing an
2035
If there is a repository in a parent directory of the location, then
2036
the history of the branch will be stored in the repository. Otherwise
2037
init creates a standalone branch which carries its own history
2038
in the .bzr directory.
2040
If there is already a branch at the location but it has no working tree,
2041
the tree can be populated with 'brz checkout'.
2043
Recipe for importing a tree of files::
2049
brz commit -m "imported project"
2052
_see_also = ['init-repository', 'branch', 'checkout']
2053
takes_args = ['location?']
2055
Option('create-prefix',
2056
help='Create the path leading up to the branch '
2057
'if it does not already exist.'),
2058
RegistryOption('format',
2059
help='Specify a format for this branch. '
2060
'See "help formats" for a full list.',
2061
lazy_registry=('breezy.controldir', 'format_registry'),
2062
converter=lambda name: controldir.format_registry.make_controldir(name),
2063
value_switches=True,
2064
title="Branch format",
2066
Option('append-revisions-only',
2067
help='Never change revnos or the existing log.'
2068
' Append revisions to it only.'),
2070
'Create a branch without a working tree.')
2072
def run(self, location=None, format=None, append_revisions_only=False,
2073
create_prefix=False, no_tree=False):
2075
format = controldir.format_registry.make_controldir('default')
2076
if location is None:
2079
to_transport = transport.get_transport(location)
2081
# The path has to exist to initialize a
2082
# branch inside of it.
2083
# Just using os.mkdir, since I don't
2084
# believe that we want to create a bunch of
2085
# locations if the user supplies an extended path
2087
to_transport.ensure_base()
2088
except errors.NoSuchFile:
2089
if not create_prefix:
2090
raise errors.BzrCommandError(gettext("Parent directory of %s"
2092
"\nYou may supply --create-prefix to create all"
2093
" leading parent directories.")
2095
to_transport.create_prefix()
2098
a_controldir = controldir.ControlDir.open_from_transport(to_transport)
2099
except errors.NotBranchError:
2100
# really a NotBzrDir error...
2101
create_branch = controldir.ControlDir.create_branch_convenience
2103
force_new_tree = False
2105
force_new_tree = None
2106
branch = create_branch(to_transport.base, format=format,
2107
possible_transports=[to_transport],
2108
force_new_tree=force_new_tree)
2109
a_controldir = branch.controldir
2111
from .transport.local import LocalTransport
2112
if a_controldir.has_branch():
2113
if (isinstance(to_transport, LocalTransport)
2114
and not a_controldir.has_workingtree()):
2115
raise errors.BranchExistsWithoutWorkingTree(location)
2116
raise errors.AlreadyBranchError(location)
2117
branch = a_controldir.create_branch()
2118
if not no_tree and not a_controldir.has_workingtree():
2119
a_controldir.create_workingtree()
2120
if append_revisions_only:
2122
branch.set_append_revisions_only(True)
2123
except errors.UpgradeRequired:
2124
raise errors.BzrCommandError(gettext('This branch format cannot be set'
2125
' to append-revisions-only. Try --default.'))
2127
from .info import describe_layout, describe_format
2129
tree = a_controldir.open_workingtree(recommend_upgrade=False)
2130
except (errors.NoWorkingTree, errors.NotLocalUrl):
2132
repository = branch.repository
2133
layout = describe_layout(repository, branch, tree).lower()
2134
format = describe_format(a_controldir, repository, branch, tree)
2135
self.outf.write(gettext("Created a {0} (format: {1})\n").format(
2137
if repository.is_shared():
2138
#XXX: maybe this can be refactored into transport.path_or_url()
2139
url = repository.controldir.root_transport.external_url()
2141
url = urlutils.local_path_from_url(url)
2142
except urlutils.InvalidURL:
2144
self.outf.write(gettext("Using shared repository: %s\n") % url)
2147
class cmd_init_repository(Command):
2148
__doc__ = """Create a shared repository for branches to share storage space.
2150
New branches created under the repository directory will store their
2151
revisions in the repository, not in the branch directory. For branches
2152
with shared history, this reduces the amount of storage needed and
2153
speeds up the creation of new branches.
2155
If the --no-trees option is given then the branches in the repository
2156
will not have working trees by default. They will still exist as
2157
directories on disk, but they will not have separate copies of the
2158
files at a certain revision. This can be useful for repositories that
2159
store branches which are interacted with through checkouts or remote
2160
branches, such as on a server.
2163
Create a shared repository holding just branches::
2165
brz init-repo --no-trees repo
2168
Make a lightweight checkout elsewhere::
2170
brz checkout --lightweight repo/trunk trunk-checkout
2175
_see_also = ['init', 'branch', 'checkout', 'repositories']
2176
takes_args = ["location"]
2177
takes_options = [RegistryOption('format',
2178
help='Specify a format for this repository. See'
2179
' "brz help formats" for details.',
2180
lazy_registry=('breezy.controldir', 'format_registry'),
2181
converter=lambda name: controldir.format_registry.make_controldir(name),
2182
value_switches=True, title='Repository format'),
2184
help='Branches in the repository will default to'
2185
' not having a working tree.'),
2187
aliases = ["init-repo"]
2189
def run(self, location, format=None, no_trees=False):
2191
format = controldir.format_registry.make_controldir('default')
2193
if location is None:
2196
to_transport = transport.get_transport(location)
2198
if format.fixed_components:
2199
repo_format_name = None
2201
repo_format_name = format.repository_format.get_format_string()
2203
(repo, newdir, require_stacking, repository_policy) = (
2204
format.initialize_on_transport_ex(to_transport,
2205
create_prefix=True, make_working_trees=not no_trees,
2206
shared_repo=True, force_new_repo=True,
2207
use_existing_dir=True,
2208
repo_format_name=repo_format_name))
2210
from .info import show_bzrdir_info
2211
show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
2214
class cmd_diff(Command):
2215
__doc__ = """Show differences in the working tree, between revisions or branches.
2217
If no arguments are given, all changes for the current tree are listed.
2218
If files are given, only the changes in those files are listed.
2219
Remote and multiple branches can be compared by using the --old and
2220
--new options. If not provided, the default for both is derived from
2221
the first argument, if any, or the current tree if no arguments are
2224
"brz diff -p1" is equivalent to "brz diff --prefix old/:new/", and
2225
produces patches suitable for "patch -p1".
2227
Note that when using the -r argument with a range of revisions, the
2228
differences are computed between the two specified revisions. That
2229
is, the command does not show the changes introduced by the first
2230
revision in the range. This differs from the interpretation of
2231
revision ranges used by "brz log" which includes the first revision
2236
2 - unrepresentable changes
2241
Shows the difference in the working tree versus the last commit::
2245
Difference between the working tree and revision 1::
2249
Difference between revision 3 and revision 1::
2253
Difference between revision 3 and revision 1 for branch xxx::
2257
The changes introduced by revision 2 (equivalent to -r1..2)::
2261
To see the changes introduced by revision X::
2265
Note that in the case of a merge, the -c option shows the changes
2266
compared to the left hand parent. To see the changes against
2267
another parent, use::
2269
brz diff -r<chosen_parent>..X
2271
The changes between the current revision and the previous revision
2272
(equivalent to -c-1 and -r-2..-1)
2276
Show just the differences for file NEWS::
2280
Show the differences in working tree xxx for file NEWS::
2284
Show the differences from branch xxx to this working tree:
2288
Show the differences between two branches for file NEWS::
2290
brz diff --old xxx --new yyy NEWS
2292
Same as 'brz diff' but prefix paths with old/ and new/::
2294
brz diff --prefix old/:new/
2296
Show the differences using a custom diff program with options::
2298
brz diff --using /usr/bin/diff --diff-options -wu
2300
_see_also = ['status']
2301
takes_args = ['file*']
2303
Option('diff-options', type=text_type,
2304
help='Pass these options to the external diff program.'),
2305
Option('prefix', type=text_type,
2307
help='Set prefixes added to old and new filenames, as '
2308
'two values separated by a colon. (eg "old/:new/").'),
2310
help='Branch/tree to compare from.',
2314
help='Branch/tree to compare to.',
2320
help='Use this command to compare files.',
2323
RegistryOption('format',
2325
help='Diff format to use.',
2326
lazy_registry=('breezy.diff', 'format_registry'),
2327
title='Diff format'),
2329
help='How many lines of context to show.',
2333
aliases = ['di', 'dif']
2334
encoding_type = 'exact'
2337
def run(self, revision=None, file_list=None, diff_options=None,
2338
prefix=None, old=None, new=None, using=None, format=None,
2340
from .diff import (get_trees_and_branches_to_diff_locked,
2347
elif prefix == u'1' or prefix is None:
2350
elif u':' in prefix:
2351
old_label, new_label = prefix.split(u":")
2353
raise errors.BzrCommandError(gettext(
2354
'--prefix expects two values separated by a colon'
2355
' (eg "old/:new/")'))
2357
if revision and len(revision) > 2:
2358
raise errors.BzrCommandError(gettext('brz diff --revision takes exactly'
2359
' one or two revision specifiers'))
2361
if using is not None and format is not None:
2362
raise errors.BzrCommandError(gettext(
2363
'{0} and {1} are mutually exclusive').format(
2364
'--using', '--format'))
2366
(old_tree, new_tree,
2367
old_branch, new_branch,
2368
specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
2369
file_list, revision, old, new, self.add_cleanup, apply_view=True)
2370
# GNU diff on Windows uses ANSI encoding for filenames
2371
path_encoding = osutils.get_diff_header_encoding()
2372
return show_diff_trees(old_tree, new_tree, self.outf,
2373
specific_files=specific_files,
2374
external_diff_options=diff_options,
2375
old_label=old_label, new_label=new_label,
2376
extra_trees=extra_trees,
2377
path_encoding=path_encoding,
2378
using=using, context=context,
2382
class cmd_deleted(Command):
2383
__doc__ = """List files deleted in the working tree.
2385
# TODO: Show files deleted since a previous revision, or
2386
# between two revisions.
2387
# TODO: Much more efficient way to do this: read in new
2388
# directories with readdir, rather than stating each one. Same
2389
# level of effort but possibly much less IO. (Or possibly not,
2390
# if the directories are very large...)
2391
_see_also = ['status', 'ls']
2392
takes_options = ['directory', 'show-ids']
2395
def run(self, show_ids=False, directory=u'.'):
2396
tree = WorkingTree.open_containing(directory)[0]
2397
self.add_cleanup(tree.lock_read().unlock)
2398
old = tree.basis_tree()
2399
self.add_cleanup(old.lock_read().unlock)
2400
for path, ie in old.iter_entries_by_dir():
2401
if not tree.has_id(ie.file_id):
2402
self.outf.write(path)
2404
self.outf.write(' ')
2405
self.outf.write(ie.file_id)
2406
self.outf.write('\n')
2409
class cmd_modified(Command):
2410
__doc__ = """List files modified in working tree.
2414
_see_also = ['status', 'ls']
2415
takes_options = ['directory', 'null']
2418
def run(self, null=False, directory=u'.'):
2419
tree = WorkingTree.open_containing(directory)[0]
2420
self.add_cleanup(tree.lock_read().unlock)
2421
td = tree.changes_from(tree.basis_tree())
2423
for path, id, kind, text_modified, meta_modified in td.modified:
2425
self.outf.write(path + '\0')
2427
self.outf.write(osutils.quotefn(path) + '\n')
2430
class cmd_added(Command):
2431
__doc__ = """List files added in working tree.
2435
_see_also = ['status', 'ls']
2436
takes_options = ['directory', 'null']
2439
def run(self, null=False, directory=u'.'):
2440
wt = WorkingTree.open_containing(directory)[0]
2441
self.add_cleanup(wt.lock_read().unlock)
2442
basis = wt.basis_tree()
2443
self.add_cleanup(basis.lock_read().unlock)
2444
root_id = wt.get_root_id()
2445
for path in wt.all_versioned_paths():
2446
if basis.has_filename(path):
2450
if not os.access(osutils.pathjoin(wt.basedir, path), os.F_OK):
2453
self.outf.write(path + '\0')
2455
self.outf.write(osutils.quotefn(path) + '\n')
2458
class cmd_root(Command):
2459
__doc__ = """Show the tree root directory.
2461
The root is the nearest enclosing directory with a .bzr control
2464
takes_args = ['filename?']
2466
def run(self, filename=None):
2467
"""Print the branch root."""
2468
tree = WorkingTree.open_containing(filename)[0]
2469
self.outf.write(tree.basedir + '\n')
2472
def _parse_limit(limitstring):
2474
return int(limitstring)
2476
msg = gettext("The limit argument must be an integer.")
2477
raise errors.BzrCommandError(msg)
2480
def _parse_levels(s):
2484
msg = gettext("The levels argument must be an integer.")
2485
raise errors.BzrCommandError(msg)
2488
class cmd_log(Command):
2489
__doc__ = """Show historical log for a branch or subset of a branch.
2491
log is brz's default tool for exploring the history of a branch.
2492
The branch to use is taken from the first parameter. If no parameters
2493
are given, the branch containing the working directory is logged.
2494
Here are some simple examples::
2496
brz log log the current branch
2497
brz log foo.py log a file in its branch
2498
brz log http://server/branch log a branch on a server
2500
The filtering, ordering and information shown for each revision can
2501
be controlled as explained below. By default, all revisions are
2502
shown sorted (topologically) so that newer revisions appear before
2503
older ones and descendants always appear before ancestors. If displayed,
2504
merged revisions are shown indented under the revision in which they
2509
The log format controls how information about each revision is
2510
displayed. The standard log formats are called ``long``, ``short``
2511
and ``line``. The default is long. See ``brz help log-formats``
2512
for more details on log formats.
2514
The following options can be used to control what information is
2517
-l N display a maximum of N revisions
2518
-n N display N levels of revisions (0 for all, 1 for collapsed)
2519
-v display a status summary (delta) for each revision
2520
-p display a diff (patch) for each revision
2521
--show-ids display revision-ids (and file-ids), not just revnos
2523
Note that the default number of levels to display is a function of the
2524
log format. If the -n option is not used, the standard log formats show
2525
just the top level (mainline).
2527
Status summaries are shown using status flags like A, M, etc. To see
2528
the changes explained using words like ``added`` and ``modified``
2529
instead, use the -vv option.
2533
To display revisions from oldest to newest, use the --forward option.
2534
In most cases, using this option will have little impact on the total
2535
time taken to produce a log, though --forward does not incrementally
2536
display revisions like --reverse does when it can.
2538
:Revision filtering:
2540
The -r option can be used to specify what revision or range of revisions
2541
to filter against. The various forms are shown below::
2543
-rX display revision X
2544
-rX.. display revision X and later
2545
-r..Y display up to and including revision Y
2546
-rX..Y display from X to Y inclusive
2548
See ``brz help revisionspec`` for details on how to specify X and Y.
2549
Some common examples are given below::
2551
-r-1 show just the tip
2552
-r-10.. show the last 10 mainline revisions
2553
-rsubmit:.. show what's new on this branch
2554
-rancestor:path.. show changes since the common ancestor of this
2555
branch and the one at location path
2556
-rdate:yesterday.. show changes since yesterday
2558
When logging a range of revisions using -rX..Y, log starts at
2559
revision Y and searches back in history through the primary
2560
("left-hand") parents until it finds X. When logging just the
2561
top level (using -n1), an error is reported if X is not found
2562
along the way. If multi-level logging is used (-n0), X may be
2563
a nested merge revision and the log will be truncated accordingly.
2567
If parameters are given and the first one is not a branch, the log
2568
will be filtered to show only those revisions that changed the
2569
nominated files or directories.
2571
Filenames are interpreted within their historical context. To log a
2572
deleted file, specify a revision range so that the file existed at
2573
the end or start of the range.
2575
Historical context is also important when interpreting pathnames of
2576
renamed files/directories. Consider the following example:
2578
* revision 1: add tutorial.txt
2579
* revision 2: modify tutorial.txt
2580
* revision 3: rename tutorial.txt to guide.txt; add tutorial.txt
2584
* ``brz log guide.txt`` will log the file added in revision 1
2586
* ``brz log tutorial.txt`` will log the new file added in revision 3
2588
* ``brz log -r2 -p tutorial.txt`` will show the changes made to
2589
the original file in revision 2.
2591
* ``brz log -r2 -p guide.txt`` will display an error message as there
2592
was no file called guide.txt in revision 2.
2594
Renames are always followed by log. By design, there is no need to
2595
explicitly ask for this (and no way to stop logging a file back
2596
until it was last renamed).
2600
The --match option can be used for finding revisions that match a
2601
regular expression in a commit message, committer, author or bug.
2602
Specifying the option several times will match any of the supplied
2603
expressions. --match-author, --match-bugs, --match-committer and
2604
--match-message can be used to only match a specific field.
2608
GUI tools and IDEs are often better at exploring history than command
2609
line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2610
bzr-explorer shell, or the Loggerhead web interface. See the Bazaar
2611
Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2612
<http://wiki.bazaar.canonical.com/IDEIntegration>.
2614
You may find it useful to add the aliases below to ``breezy.conf``::
2618
top = log -l10 --line
2621
``brz tip`` will then show the latest revision while ``brz top``
2622
will show the last 10 mainline revisions. To see the details of a
2623
particular revision X, ``brz show -rX``.
2625
If you are interested in looking deeper into a particular merge X,
2626
use ``brz log -n0 -rX``.
2628
``brz log -v`` on a branch with lots of history is currently
2629
very slow. A fix for this issue is currently under development.
2630
With or without that fix, it is recommended that a revision range
2631
be given when using the -v option.
2633
brz has a generic full-text matching plugin, brz-search, that can be
2634
used to find revisions matching user names, commit messages, etc.
2635
Among other features, this plugin can find all revisions containing
2636
a list of words but not others.
2638
When exploring non-mainline history on large projects with deep
2639
history, the performance of log can be greatly improved by installing
2640
the historycache plugin. This plugin buffers historical information
2641
trading disk space for faster speed.
2643
takes_args = ['file*']
2644
_see_also = ['log-formats', 'revisionspec']
2647
help='Show from oldest to newest.'),
2649
custom_help('verbose',
2650
help='Show files changed in each revision.'),
2654
type=breezy.option._parse_revision_str,
2656
help='Show just the specified revision.'
2657
' See also "help revisionspec".'),
2659
RegistryOption('authors',
2660
'What names to list as authors - first, all or committer.',
2662
lazy_registry=('breezy.log', 'author_list_registry'),
2666
help='Number of levels to display - 0 for all, 1 for flat.',
2668
type=_parse_levels),
2670
help='Show revisions whose message matches this '
2671
'regular expression.',
2676
help='Limit the output to the first N revisions.',
2681
help='Show changes made in each revision as a patch.'),
2682
Option('include-merged',
2683
help='Show merged revisions like --levels 0 does.'),
2684
Option('include-merges', hidden=True,
2685
help='Historical alias for --include-merged.'),
2686
Option('omit-merges',
2687
help='Do not report commits with more than one parent.'),
2688
Option('exclude-common-ancestry',
2689
help='Display only the revisions that are not part'
2690
' of both ancestries (require -rX..Y).'
2692
Option('signatures',
2693
help='Show digital signature validity.'),
2696
help='Show revisions whose properties match this '
2699
ListOption('match-message',
2700
help='Show revisions whose message matches this '
2703
ListOption('match-committer',
2704
help='Show revisions whose committer matches this '
2707
ListOption('match-author',
2708
help='Show revisions whose authors match this '
2711
ListOption('match-bugs',
2712
help='Show revisions whose bugs match this '
2716
encoding_type = 'replace'
2719
def run(self, file_list=None, timezone='original',
2730
include_merged=None,
2732
exclude_common_ancestry=False,
2736
match_committer=None,
2743
make_log_request_dict,
2744
_get_info_for_log_files,
2746
direction = (forward and 'forward') or 'reverse'
2747
if include_merged is None:
2748
include_merged = False
2749
if (exclude_common_ancestry
2750
and (revision is None or len(revision) != 2)):
2751
raise errors.BzrCommandError(gettext(
2752
'--exclude-common-ancestry requires -r with two revisions'))
2757
raise errors.BzrCommandError(gettext(
2758
'{0} and {1} are mutually exclusive').format(
2759
'--levels', '--include-merged'))
2761
if change is not None:
2763
raise errors.RangeInChangeOption()
2764
if revision is not None:
2765
raise errors.BzrCommandError(gettext(
2766
'{0} and {1} are mutually exclusive').format(
2767
'--revision', '--change'))
2772
filter_by_dir = False
2774
# find the file ids to log and check for directory filtering
2775
b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2776
revision, file_list, self.add_cleanup)
2777
for relpath, file_id, kind in file_info_list:
2779
raise errors.BzrCommandError(gettext(
2780
"Path unknown at end or start of revision range: %s") %
2782
# If the relpath is the top of the tree, we log everything
2787
file_ids.append(file_id)
2788
filter_by_dir = filter_by_dir or (
2789
kind in ['directory', 'tree-reference'])
2792
# FIXME ? log the current subdir only RBC 20060203
2793
if revision is not None \
2794
and len(revision) > 0 and revision[0].get_branch():
2795
location = revision[0].get_branch()
2798
dir, relpath = controldir.ControlDir.open_containing(location)
2799
b = dir.open_branch()
2800
self.add_cleanup(b.lock_read().unlock)
2801
rev1, rev2 = _get_revision_range(revision, b, self.name())
2803
if b.get_config_stack().get('validate_signatures_in_log'):
2807
if not gpg.GPGStrategy.verify_signatures_available():
2808
raise errors.GpgmeNotInstalled(None)
2810
# Decide on the type of delta & diff filtering to use
2811
# TODO: add an --all-files option to make this configurable & consistent
2819
diff_type = 'partial'
2823
# Build the log formatter
2824
if log_format is None:
2825
log_format = log.log_formatter_registry.get_default(b)
2826
# Make a non-encoding output to include the diffs - bug 328007
2827
unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2828
lf = log_format(show_ids=show_ids, to_file=self.outf,
2829
to_exact_file=unencoded_output,
2830
show_timezone=timezone,
2831
delta_format=get_verbosity_level(),
2833
show_advice=levels is None,
2834
author_list_handler=authors)
2836
# Choose the algorithm for doing the logging. It's annoying
2837
# having multiple code paths like this but necessary until
2838
# the underlying repository format is faster at generating
2839
# deltas or can provide everything we need from the indices.
2840
# The default algorithm - match-using-deltas - works for
2841
# multiple files and directories and is faster for small
2842
# amounts of history (200 revisions say). However, it's too
2843
# slow for logging a single file in a repository with deep
2844
# history, i.e. > 10K revisions. In the spirit of "do no
2845
# evil when adding features", we continue to use the
2846
# original algorithm - per-file-graph - for the "single
2847
# file that isn't a directory without showing a delta" case.
2848
partial_history = revision and b.repository._format.supports_chks
2849
match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2850
or delta_type or partial_history)
2854
match_dict[''] = match
2856
match_dict['message'] = match_message
2858
match_dict['committer'] = match_committer
2860
match_dict['author'] = match_author
2862
match_dict['bugs'] = match_bugs
2864
# Build the LogRequest and execute it
2865
if len(file_ids) == 0:
2867
rqst = make_log_request_dict(
2868
direction=direction, specific_fileids=file_ids,
2869
start_revision=rev1, end_revision=rev2, limit=limit,
2870
message_search=message, delta_type=delta_type,
2871
diff_type=diff_type, _match_using_deltas=match_using_deltas,
2872
exclude_common_ancestry=exclude_common_ancestry, match=match_dict,
2873
signature=signatures, omit_merges=omit_merges,
2875
Logger(b, rqst).show(lf)
2878
def _get_revision_range(revisionspec_list, branch, command_name):
2879
"""Take the input of a revision option and turn it into a revision range.
2881
It returns RevisionInfo objects which can be used to obtain the rev_id's
2882
of the desired revisions. It does some user input validations.
2884
if revisionspec_list is None:
2887
elif len(revisionspec_list) == 1:
2888
rev1 = rev2 = revisionspec_list[0].in_history(branch)
2889
elif len(revisionspec_list) == 2:
2890
start_spec = revisionspec_list[0]
2891
end_spec = revisionspec_list[1]
2892
if end_spec.get_branch() != start_spec.get_branch():
2893
# b is taken from revision[0].get_branch(), and
2894
# show_log will use its revision_history. Having
2895
# different branches will lead to weird behaviors.
2896
raise errors.BzrCommandError(gettext(
2897
"brz %s doesn't accept two revisions in different"
2898
" branches.") % command_name)
2899
if start_spec.spec is None:
2900
# Avoid loading all the history.
2901
rev1 = RevisionInfo(branch, None, None)
2903
rev1 = start_spec.in_history(branch)
2904
# Avoid loading all of history when we know a missing
2905
# end of range means the last revision ...
2906
if end_spec.spec is None:
2907
last_revno, last_revision_id = branch.last_revision_info()
2908
rev2 = RevisionInfo(branch, last_revno, last_revision_id)
2910
rev2 = end_spec.in_history(branch)
2912
raise errors.BzrCommandError(gettext(
2913
'brz %s --revision takes one or two values.') % command_name)
2917
def _revision_range_to_revid_range(revision_range):
2920
if revision_range[0] is not None:
2921
rev_id1 = revision_range[0].rev_id
2922
if revision_range[1] is not None:
2923
rev_id2 = revision_range[1].rev_id
2924
return rev_id1, rev_id2
2926
def get_log_format(long=False, short=False, line=False, default='long'):
2927
log_format = default
2931
log_format = 'short'
2937
class cmd_touching_revisions(Command):
2938
__doc__ = """Return revision-ids which affected a particular file.
2940
A more user-friendly interface is "brz log FILE".
2944
takes_args = ["filename"]
2947
def run(self, filename):
2948
tree, relpath = WorkingTree.open_containing(filename)
2949
with tree.lock_read():
2950
touching_revs = log.find_touching_revisions(
2951
tree.branch.repository, tree.branch.last_revision(), tree, relpath)
2952
for revno, revision_id, what in reversed(list(touching_revs)):
2953
self.outf.write("%6d %s\n" % (revno, what))
2956
class cmd_ls(Command):
2957
__doc__ = """List files in a tree.
2960
_see_also = ['status', 'cat']
2961
takes_args = ['path?']
2965
Option('recursive', short_name='R',
2966
help='Recurse into subdirectories.'),
2968
help='Print paths relative to the root of the branch.'),
2969
Option('unknown', short_name='u',
2970
help='Print unknown files.'),
2971
Option('versioned', help='Print versioned files.',
2973
Option('ignored', short_name='i',
2974
help='Print ignored files.'),
2975
Option('kind', short_name='k',
2976
help='List entries of a particular kind: file, directory, symlink.',
2983
def run(self, revision=None, verbose=False,
2984
recursive=False, from_root=False,
2985
unknown=False, versioned=False, ignored=False,
2986
null=False, kind=None, show_ids=False, path=None, directory=None):
2988
if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
2989
raise errors.BzrCommandError(gettext('invalid kind specified'))
2991
if verbose and null:
2992
raise errors.BzrCommandError(gettext('Cannot set both --verbose and --null'))
2993
all = not (unknown or versioned or ignored)
2995
selection = {'I':ignored, '?':unknown, 'V':versioned}
3001
raise errors.BzrCommandError(gettext('cannot specify both --from-root'
3004
tree, branch, relpath = \
3005
_open_directory_or_containing_tree_or_branch(fs_path, directory)
3007
# Calculate the prefix to use
3011
prefix = relpath + '/'
3012
elif fs_path != '.' and not fs_path.endswith('/'):
3013
prefix = fs_path + '/'
3015
if revision is not None or tree is None:
3016
tree = _get_one_revision_tree('ls', revision, branch=branch)
3019
if isinstance(tree, WorkingTree) and tree.supports_views():
3020
view_files = tree.views.lookup_view()
3023
view_str = views.view_display_str(view_files)
3024
note(gettext("Ignoring files outside view. View is %s") % view_str)
3026
self.add_cleanup(tree.lock_read().unlock)
3027
for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
3028
from_dir=relpath, recursive=recursive):
3029
# Apply additional masking
3030
if not all and not selection[fc]:
3032
if kind is not None and fkind != kind:
3037
fullpath = osutils.pathjoin(relpath, fp)
3040
views.check_path_in_view(tree, fullpath)
3041
except views.FileOutsideView:
3046
fp = osutils.pathjoin(prefix, fp)
3047
kindch = entry.kind_character()
3048
outstring = fp + kindch
3049
ui.ui_factory.clear_term()
3051
outstring = '%-8s %s' % (fc, outstring)
3052
if show_ids and fid is not None:
3053
outstring = "%-50s %s" % (outstring, fid.decode('utf-8'))
3054
self.outf.write(outstring + '\n')
3056
self.outf.write(fp + '\0')
3059
self.outf.write(fid.decode('utf-8'))
3060
self.outf.write('\0')
3065
my_id = fid.decode('utf-8')
3068
self.outf.write('%-50s %s\n' % (outstring, my_id))
3070
self.outf.write(outstring + '\n')
3073
class cmd_unknowns(Command):
3074
__doc__ = """List unknown files.
3079
takes_options = ['directory']
3082
def run(self, directory=u'.'):
3083
for f in WorkingTree.open_containing(directory)[0].unknowns():
3084
self.outf.write(osutils.quotefn(f) + '\n')
3087
class cmd_ignore(Command):
3088
__doc__ = """Ignore specified files or patterns.
3090
See ``brz help patterns`` for details on the syntax of patterns.
3092
If a .bzrignore file does not exist, the ignore command
3093
will create one and add the specified files or patterns to the newly
3094
created file. The ignore command will also automatically add the
3095
.bzrignore file to be versioned. Creating a .bzrignore file without
3096
the use of the ignore command will require an explicit add command.
3098
To remove patterns from the ignore list, edit the .bzrignore file.
3099
After adding, editing or deleting that file either indirectly by
3100
using this command or directly by using an editor, be sure to commit
3103
Bazaar also supports a global ignore file ~/.bazaar/ignore. On Windows
3104
the global ignore file can be found in the application data directory as
3105
C:\\Documents and Settings\\<user>\\Application Data\\Bazaar\\2.0\\ignore.
3106
Global ignores are not touched by this command. The global ignore file
3107
can be edited directly using an editor.
3109
Patterns prefixed with '!' are exceptions to ignore patterns and take
3110
precedence over regular ignores. Such exceptions are used to specify
3111
files that should be versioned which would otherwise be ignored.
3113
Patterns prefixed with '!!' act as regular ignore patterns, but have
3114
precedence over the '!' exception patterns.
3118
* Ignore patterns containing shell wildcards must be quoted from
3121
* Ignore patterns starting with "#" act as comments in the ignore file.
3122
To ignore patterns that begin with that character, use the "RE:" prefix.
3125
Ignore the top level Makefile::
3127
brz ignore ./Makefile
3129
Ignore .class files in all directories...::
3131
brz ignore "*.class"
3133
...but do not ignore "special.class"::
3135
brz ignore "!special.class"
3137
Ignore files whose name begins with the "#" character::
3141
Ignore .o files under the lib directory::
3143
brz ignore "lib/**/*.o"
3145
Ignore .o files under the lib directory::
3147
brz ignore "RE:lib/.*\\.o"
3149
Ignore everything but the "debian" toplevel directory::
3151
brz ignore "RE:(?!debian/).*"
3153
Ignore everything except the "local" toplevel directory,
3154
but always ignore autosave files ending in ~, even under local/::
3157
brz ignore "!./local"
3161
_see_also = ['status', 'ignored', 'patterns']
3162
takes_args = ['name_pattern*']
3163
takes_options = ['directory',
3164
Option('default-rules',
3165
help='Display the default ignore rules that brz uses.')
3168
def run(self, name_pattern_list=None, default_rules=None,
3170
from breezy import ignores
3171
if default_rules is not None:
3172
# dump the default rules and exit
3173
for pattern in ignores.USER_DEFAULTS:
3174
self.outf.write("%s\n" % pattern)
3176
if not name_pattern_list:
3177
raise errors.BzrCommandError(gettext("ignore requires at least one "
3178
"NAME_PATTERN or --default-rules."))
3179
name_pattern_list = [globbing.normalize_pattern(p)
3180
for p in name_pattern_list]
3182
bad_patterns_count = 0
3183
for p in name_pattern_list:
3184
if not globbing.Globster.is_pattern_valid(p):
3185
bad_patterns_count += 1
3186
bad_patterns += ('\n %s' % p)
3188
msg = (ngettext('Invalid ignore pattern found. %s',
3189
'Invalid ignore patterns found. %s',
3190
bad_patterns_count) % bad_patterns)
3191
ui.ui_factory.show_error(msg)
3192
raise lazy_regex.InvalidPattern('')
3193
for name_pattern in name_pattern_list:
3194
if (name_pattern[0] == '/' or
3195
(len(name_pattern) > 1 and name_pattern[1] == ':')):
3196
raise errors.BzrCommandError(gettext(
3197
"NAME_PATTERN should not be an absolute path"))
3198
tree, relpath = WorkingTree.open_containing(directory)
3199
ignores.tree_ignores_add_patterns(tree, name_pattern_list)
3200
ignored = globbing.Globster(name_pattern_list)
3202
self.add_cleanup(tree.lock_read().unlock)
3203
for entry in tree.list_files():
3207
if ignored.match(filename):
3208
matches.append(filename)
3209
if len(matches) > 0:
3210
self.outf.write(gettext("Warning: the following files are version "
3211
"controlled and match your ignore pattern:\n%s"
3212
"\nThese files will continue to be version controlled"
3213
" unless you 'brz remove' them.\n") % ("\n".join(matches),))
3216
class cmd_ignored(Command):
3217
__doc__ = """List ignored files and the patterns that matched them.
3219
List all the ignored files and the ignore pattern that caused the file to
3222
Alternatively, to list just the files::
3227
encoding_type = 'replace'
3228
_see_also = ['ignore', 'ls']
3229
takes_options = ['directory']
3232
def run(self, directory=u'.'):
3233
tree = WorkingTree.open_containing(directory)[0]
3234
self.add_cleanup(tree.lock_read().unlock)
3235
for path, file_class, kind, file_id, entry in tree.list_files():
3236
if file_class != 'I':
3238
## XXX: Slightly inefficient since this was already calculated
3239
pat = tree.is_ignored(path)
3240
self.outf.write('%-50s %s\n' % (path, pat))
3243
class cmd_lookup_revision(Command):
3244
__doc__ = """Lookup the revision-id from a revision-number
3247
brz lookup-revision 33
3250
takes_args = ['revno']
3251
takes_options = ['directory']
3254
def run(self, revno, directory=u'.'):
3258
raise errors.BzrCommandError(gettext("not a valid revision-number: %r")
3260
revid = WorkingTree.open_containing(directory)[0].branch.get_rev_id(revno)
3261
self.outf.write("%s\n" % revid.decode('utf-8'))
3264
class cmd_export(Command):
3265
__doc__ = """Export current or past revision to a destination directory or archive.
3267
If no revision is specified this exports the last committed revision.
3269
Format may be an "exporter" name, such as tar, tgz, tbz2. If none is
3270
given, try to find the format with the extension. If no extension
3271
is found exports to a directory (equivalent to --format=dir).
3273
If root is supplied, it will be used as the root directory inside
3274
container formats (tar, zip, etc). If it is not supplied it will default
3275
to the exported filename. The root option has no effect for 'dir' format.
3277
If branch is omitted then the branch containing the current working
3278
directory will be used.
3280
Note: Export of tree with non-ASCII filenames to zip is not supported.
3282
================= =========================
3283
Supported formats Autodetected by extension
3284
================= =========================
3287
tbz2 .tar.bz2, .tbz2
3290
================= =========================
3293
encoding_type = 'exact'
3294
takes_args = ['dest', 'branch_or_subdir?']
3295
takes_options = ['directory',
3297
help="Type of file to export to.",
3300
Option('filters', help='Apply content filters to export the '
3301
'convenient form.'),
3304
help="Name of the root directory inside the exported file."),
3305
Option('per-file-timestamps',
3306
help='Set modification time of files to that of the last '
3307
'revision in which it was changed.'),
3308
Option('uncommitted',
3309
help='Export the working tree contents rather than that of the '
3312
def run(self, dest, branch_or_subdir=None, revision=None, format=None,
3313
root=None, filters=False, per_file_timestamps=False, uncommitted=False,
3315
from .export import export, guess_format, get_root_name
3317
if branch_or_subdir is None:
3318
branch_or_subdir = directory
3320
(tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
3322
if tree is not None:
3323
self.add_cleanup(tree.lock_read().unlock)
3327
raise errors.BzrCommandError(
3328
gettext("--uncommitted requires a working tree"))
3331
export_tree = _get_one_revision_tree(
3332
'export', revision, branch=b,
3336
format = guess_format(dest)
3339
root = get_root_name(dest)
3341
if not per_file_timestamps:
3342
force_mtime = time.time()
3347
from breezy.filter_tree import ContentFilterTree
3348
export_tree = ContentFilterTree(
3349
export_tree, export_tree._content_filter_stack)
3352
export(export_tree, dest, format, root, subdir,
3353
per_file_timestamps=per_file_timestamps)
3354
except errors.NoSuchExportFormat as e:
3355
raise errors.BzrCommandError(
3356
gettext('Unsupported export format: %s') % e.format)
3359
class cmd_cat(Command):
3360
__doc__ = """Write the contents of a file as of a given revision to standard output.
3362
If no revision is nominated, the last revision is used.
3364
Note: Take care to redirect standard output when using this command on a
3369
takes_options = ['directory',
3370
Option('name-from-revision', help='The path name in the old tree.'),
3371
Option('filters', help='Apply content filters to display the '
3372
'convenience form.'),
3375
takes_args = ['filename']
3376
encoding_type = 'exact'
3379
def run(self, filename, revision=None, name_from_revision=False,
3380
filters=False, directory=None):
3381
if revision is not None and len(revision) != 1:
3382
raise errors.BzrCommandError(gettext("brz cat --revision takes exactly"
3383
" one revision specifier"))
3384
tree, branch, relpath = \
3385
_open_directory_or_containing_tree_or_branch(filename, directory)
3386
self.add_cleanup(branch.lock_read().unlock)
3387
return self._run(tree, branch, relpath, filename, revision,
3388
name_from_revision, filters)
3390
def _run(self, tree, b, relpath, filename, revision, name_from_revision,
3394
tree = b.basis_tree()
3395
rev_tree = _get_one_revision_tree('cat', revision, branch=b)
3396
self.add_cleanup(rev_tree.lock_read().unlock)
3398
old_file_id = rev_tree.path2id(relpath)
3400
# TODO: Split out this code to something that generically finds the
3401
# best id for a path across one or more trees; it's like
3402
# find_ids_across_trees but restricted to find just one. -- mbp
3404
if name_from_revision:
3405
# Try in revision if requested
3406
if old_file_id is None:
3407
raise errors.BzrCommandError(gettext(
3408
"{0!r} is not present in revision {1}").format(
3409
filename, rev_tree.get_revision_id()))
3411
actual_file_id = old_file_id
3413
cur_file_id = tree.path2id(relpath)
3414
if cur_file_id is not None and rev_tree.has_id(cur_file_id):
3415
actual_file_id = cur_file_id
3416
elif old_file_id is not None:
3417
actual_file_id = old_file_id
3419
raise errors.BzrCommandError(gettext(
3420
"{0!r} is not present in revision {1}").format(
3421
filename, rev_tree.get_revision_id()))
3422
relpath = rev_tree.id2path(actual_file_id)
3424
from .filter_tree import ContentFilterTree
3425
filter_tree = ContentFilterTree(rev_tree,
3426
rev_tree._content_filter_stack)
3427
fileobj = filter_tree.get_file(relpath, actual_file_id)
3429
fileobj = rev_tree.get_file(relpath, actual_file_id)
3430
shutil.copyfileobj(fileobj, self.outf)
3434
class cmd_local_time_offset(Command):
3435
__doc__ = """Show the offset in seconds from GMT to local time."""
3439
self.outf.write("%s\n" % osutils.local_time_offset())
3443
class cmd_commit(Command):
3444
__doc__ = """Commit changes into a new revision.
3446
An explanatory message needs to be given for each commit. This is
3447
often done by using the --message option (getting the message from the
3448
command line) or by using the --file option (getting the message from
3449
a file). If neither of these options is given, an editor is opened for
3450
the user to enter the message. To see the changed files in the
3451
boilerplate text loaded into the editor, use the --show-diff option.
3453
By default, the entire tree is committed and the person doing the
3454
commit is assumed to be the author. These defaults can be overridden
3459
If selected files are specified, only changes to those files are
3460
committed. If a directory is specified then the directory and
3461
everything within it is committed.
3463
When excludes are given, they take precedence over selected files.
3464
For example, to commit only changes within foo, but not changes
3467
brz commit foo -x foo/bar
3469
A selective commit after a merge is not yet supported.
3473
If the author of the change is not the same person as the committer,
3474
you can specify the author's name using the --author option. The
3475
name should be in the same format as a committer-id, e.g.
3476
"John Doe <jdoe@example.com>". If there is more than one author of
3477
the change you can specify the option multiple times, once for each
3482
A common mistake is to forget to add a new file or directory before
3483
running the commit command. The --strict option checks for unknown
3484
files and aborts the commit if any are found. More advanced pre-commit
3485
checks can be implemented by defining hooks. See ``brz help hooks``
3490
If you accidentially commit the wrong changes or make a spelling
3491
mistake in the commit message say, you can use the uncommit command
3492
to undo it. See ``brz help uncommit`` for details.
3494
Hooks can also be configured to run after a commit. This allows you
3495
to trigger updates to external systems like bug trackers. The --fixes
3496
option can be used to record the association between a revision and
3497
one or more bugs. See ``brz help bugs`` for details.
3500
_see_also = ['add', 'bugs', 'hooks', 'uncommit']
3501
takes_args = ['selected*']
3503
ListOption('exclude', type=text_type, short_name='x',
3504
help="Do not consider changes made to a given path."),
3505
Option('message', type=text_type,
3507
help="Description of the new revision."),
3510
help='Commit even if nothing has changed.'),
3511
Option('file', type=text_type,
3514
help='Take commit message from this file.'),
3516
help="Refuse to commit if there are unknown "
3517
"files in the working tree."),
3518
Option('commit-time', type=text_type,
3519
help="Manually set a commit time using commit date "
3520
"format, e.g. '2009-10-10 08:00:00 +0100'."),
3521
ListOption('fixes', type=text_type,
3522
help="Mark a bug as being fixed by this revision "
3523
"(see \"brz help bugs\")."),
3524
ListOption('author', type=text_type,
3525
help="Set the author's name, if it's different "
3526
"from the committer."),
3528
help="Perform a local commit in a bound "
3529
"branch. Local commits are not pushed to "
3530
"the master branch until a normal commit "
3533
Option('show-diff', short_name='p',
3534
help='When no message is supplied, show the diff along'
3535
' with the status summary in the message editor.'),
3537
help='When committing to a foreign version control '
3538
'system do not push data that can not be natively '
3541
aliases = ['ci', 'checkin']
3543
def _iter_bug_fix_urls(self, fixes, branch):
3544
default_bugtracker = None
3545
# Configure the properties for bug fixing attributes.
3546
for fixed_bug in fixes:
3547
tokens = fixed_bug.split(':')
3548
if len(tokens) == 1:
3549
if default_bugtracker is None:
3550
branch_config = branch.get_config_stack()
3551
default_bugtracker = branch_config.get(
3553
if default_bugtracker is None:
3554
raise errors.BzrCommandError(gettext(
3555
"No tracker specified for bug %s. Use the form "
3556
"'tracker:id' or specify a default bug tracker "
3557
"using the `bugtracker` option.\nSee "
3558
"\"brz help bugs\" for more information on this "
3559
"feature. Commit refused.") % fixed_bug)
3560
tag = default_bugtracker
3562
elif len(tokens) != 2:
3563
raise errors.BzrCommandError(gettext(
3564
"Invalid bug %s. Must be in the form of 'tracker:id'. "
3565
"See \"brz help bugs\" for more information on this "
3566
"feature.\nCommit refused.") % fixed_bug)
3568
tag, bug_id = tokens
3570
yield bugtracker.get_bug_url(tag, branch, bug_id)
3571
except bugtracker.UnknownBugTrackerAbbreviation:
3572
raise errors.BzrCommandError(gettext(
3573
'Unrecognized bug %s. Commit refused.') % fixed_bug)
3574
except bugtracker.MalformedBugIdentifier as e:
3575
raise errors.BzrCommandError(gettext(
3576
u"%s\nCommit refused.") % (e,))
3578
def run(self, message=None, file=None, verbose=False, selected_list=None,
3579
unchanged=False, strict=False, local=False, fixes=None,
3580
author=None, show_diff=False, exclude=None, commit_time=None,
3582
from .commit import (
3585
from .errors import (
3589
from .msgeditor import (
3590
edit_commit_message_encoded,
3591
generate_commit_message_template,
3592
make_commit_message_template_encoded,
3596
commit_stamp = offset = None
3597
if commit_time is not None:
3599
commit_stamp, offset = timestamp.parse_patch_date(commit_time)
3600
except ValueError as e:
3601
raise errors.BzrCommandError(gettext(
3602
"Could not parse --commit-time: " + str(e)))
3606
tree, selected_list = WorkingTree.open_containing_paths(selected_list)
3607
if selected_list == ['']:
3608
# workaround - commit of root of tree should be exactly the same
3609
# as just default commit in that tree, and succeed even though
3610
# selected-file merge commit is not done yet
3615
bug_property = bugtracker.encode_fixes_bug_urls(
3616
self._iter_bug_fix_urls(fixes, tree.branch))
3618
properties[u'bugs'] = bug_property
3620
if local and not tree.branch.get_bound_location():
3621
raise errors.LocalRequiresBoundBranch()
3623
if message is not None:
3625
file_exists = osutils.lexists(message)
3626
except UnicodeError:
3627
# The commit message contains unicode characters that can't be
3628
# represented in the filesystem encoding, so that can't be a
3633
'The commit message is a file name: "%(f)s".\n'
3634
'(use --file "%(f)s" to take commit message from that file)'
3636
ui.ui_factory.show_warning(warning_msg)
3638
message = message.replace('\r\n', '\n')
3639
message = message.replace('\r', '\n')
3641
raise errors.BzrCommandError(gettext(
3642
"please specify either --message or --file"))
3644
def get_message(commit_obj):
3645
"""Callback to get commit message"""
3647
with open(file, 'rb') as f:
3648
my_message = f.read().decode(osutils.get_user_encoding())
3649
elif message is not None:
3650
my_message = message
3652
# No message supplied: make one up.
3653
# text is the status of the tree
3654
text = make_commit_message_template_encoded(tree,
3655
selected_list, diff=show_diff,
3656
output_encoding=osutils.get_user_encoding())
3657
# start_message is the template generated from hooks
3658
# XXX: Warning - looks like hooks return unicode,
3659
# make_commit_message_template_encoded returns user encoding.
3660
# We probably want to be using edit_commit_message instead to
3662
my_message = set_commit_message(commit_obj)
3663
if my_message is None:
3664
start_message = generate_commit_message_template(commit_obj)
3665
my_message = edit_commit_message_encoded(text,
3666
start_message=start_message)
3667
if my_message is None:
3668
raise errors.BzrCommandError(gettext("please specify a commit"
3669
" message with either --message or --file"))
3670
if my_message == "":
3671
raise errors.BzrCommandError(gettext("Empty commit message specified."
3672
" Please specify a commit message with either"
3673
" --message or --file or leave a blank message"
3674
" with --message \"\"."))
3677
# The API permits a commit with a filter of [] to mean 'select nothing'
3678
# but the command line should not do that.
3679
if not selected_list:
3680
selected_list = None
3682
tree.commit(message_callback=get_message,
3683
specific_files=selected_list,
3684
allow_pointless=unchanged, strict=strict, local=local,
3685
reporter=None, verbose=verbose, revprops=properties,
3686
authors=author, timestamp=commit_stamp,
3688
exclude=tree.safe_relpath_files(exclude),
3690
except PointlessCommit:
3691
raise errors.BzrCommandError(gettext("No changes to commit."
3692
" Please 'brz add' the files you want to commit, or use"
3693
" --unchanged to force an empty commit."))
3694
except ConflictsInTree:
3695
raise errors.BzrCommandError(gettext('Conflicts detected in working '
3696
'tree. Use "brz conflicts" to list, "brz resolve FILE" to'
3698
except StrictCommitFailed:
3699
raise errors.BzrCommandError(gettext("Commit refused because there are"
3700
" unknown files in the working tree."))
3701
except errors.BoundBranchOutOfDate as e:
3702
e.extra_help = (gettext("\n"
3703
'To commit to master branch, run update and then commit.\n'
3704
'You can also pass --local to commit to continue working '
3709
class cmd_check(Command):
3710
__doc__ = """Validate working tree structure, branch consistency and repository history.
3712
This command checks various invariants about branch and repository storage
3713
to detect data corruption or brz bugs.
3715
The working tree and branch checks will only give output if a problem is
3716
detected. The output fields of the repository check are:
3719
This is just the number of revisions checked. It doesn't
3723
This is just the number of versionedfiles checked. It
3724
doesn't indicate a problem.
3726
unreferenced ancestors
3727
Texts that are ancestors of other texts, but
3728
are not properly referenced by the revision ancestry. This is a
3729
subtle problem that Bazaar can work around.
3732
This is the total number of unique file contents
3733
seen in the checked revisions. It does not indicate a problem.
3736
This is the total number of repeated texts seen
3737
in the checked revisions. Texts can be repeated when their file
3738
entries are modified, but the file contents are not. It does not
3741
If no restrictions are specified, all Bazaar data that is found at the given
3742
location will be checked.
3746
Check the tree and branch at 'foo'::
3748
brz check --tree --branch foo
3750
Check only the repository at 'bar'::
3752
brz check --repo bar
3754
Check everything at 'baz'::
3759
_see_also = ['reconcile']
3760
takes_args = ['path?']
3761
takes_options = ['verbose',
3762
Option('branch', help="Check the branch related to the"
3763
" current directory."),
3764
Option('repo', help="Check the repository related to the"
3765
" current directory."),
3766
Option('tree', help="Check the working tree related to"
3767
" the current directory.")]
3769
def run(self, path=None, verbose=False, branch=False, repo=False,
3771
from .check import check_dwim
3774
if not branch and not repo and not tree:
3775
branch = repo = tree = True
3776
check_dwim(path, verbose, do_branch=branch, do_repo=repo, do_tree=tree)
3779
class cmd_upgrade(Command):
3780
__doc__ = """Upgrade a repository, branch or working tree to a newer format.
3782
When the default format has changed after a major new release of
3783
Bazaar, you may be informed during certain operations that you
3784
should upgrade. Upgrading to a newer format may improve performance
3785
or make new features available. It may however limit interoperability
3786
with older repositories or with older versions of Bazaar.
3788
If you wish to upgrade to a particular format rather than the
3789
current default, that can be specified using the --format option.
3790
As a consequence, you can use the upgrade command this way to
3791
"downgrade" to an earlier format, though some conversions are
3792
a one way process (e.g. changing from the 1.x default to the
3793
2.x default) so downgrading is not always possible.
3795
A backup.bzr.~#~ directory is created at the start of the conversion
3796
process (where # is a number). By default, this is left there on
3797
completion. If the conversion fails, delete the new .bzr directory
3798
and rename this one back in its place. Use the --clean option to ask
3799
for the backup.bzr directory to be removed on successful conversion.
3800
Alternatively, you can delete it by hand if everything looks good
3803
If the location given is a shared repository, dependent branches
3804
are also converted provided the repository converts successfully.
3805
If the conversion of a branch fails, remaining branches are still
3808
For more information on upgrades, see the Bazaar Upgrade Guide,
3809
http://doc.bazaar.canonical.com/latest/en/upgrade-guide/.
3812
_see_also = ['check', 'reconcile', 'formats']
3813
takes_args = ['url?']
3815
RegistryOption('format',
3816
help='Upgrade to a specific format. See "brz help'
3817
' formats" for details.',
3818
lazy_registry=('breezy.controldir', 'format_registry'),
3819
converter=lambda name: controldir.format_registry.make_controldir(name),
3820
value_switches=True, title='Branch format'),
3822
help='Remove the backup.bzr directory if successful.'),
3824
help="Show what would be done, but don't actually do anything."),
3827
def run(self, url='.', format=None, clean=False, dry_run=False):
3828
from .upgrade import upgrade
3829
exceptions = upgrade(url, format, clean_up=clean, dry_run=dry_run)
3831
if len(exceptions) == 1:
3832
# Compatibility with historical behavior
3838
class cmd_whoami(Command):
3839
__doc__ = """Show or set brz user id.
3842
Show the email of the current user::
3846
Set the current user::
3848
brz whoami "Frank Chu <fchu@example.com>"
3850
takes_options = [ 'directory',
3852
help='Display email address only.'),
3854
help='Set identity for the current branch instead of '
3857
takes_args = ['name?']
3858
encoding_type = 'replace'
3861
def run(self, email=False, branch=False, name=None, directory=None):
3863
if directory is None:
3864
# use branch if we're inside one; otherwise global config
3866
c = Branch.open_containing(u'.')[0].get_config_stack()
3867
except errors.NotBranchError:
3868
c = _mod_config.GlobalStack()
3870
c = Branch.open(directory).get_config_stack()
3871
identity = c.get('email')
3873
self.outf.write(_mod_config.extract_email_address(identity)
3876
self.outf.write(identity + '\n')
3880
raise errors.BzrCommandError(gettext("--email can only be used to display existing "
3883
# display a warning if an email address isn't included in the given name.
3885
_mod_config.extract_email_address(name)
3886
except _mod_config.NoEmailInUsername as e:
3887
warning('"%s" does not seem to contain an email address. '
3888
'This is allowed, but not recommended.', name)
3890
# use global config unless --branch given
3892
if directory is None:
3893
c = Branch.open_containing(u'.')[0].get_config_stack()
3895
b = Branch.open(directory)
3896
self.add_cleanup(b.lock_write().unlock)
3897
c = b.get_config_stack()
3899
c = _mod_config.GlobalStack()
3900
c.set('email', name)
3903
class cmd_nick(Command):
3904
__doc__ = """Print or set the branch nickname.
3906
If unset, the colocated branch name is used for colocated branches, and
3907
the branch directory name is used for other branches. To print the
3908
current nickname, execute with no argument.
3910
Bound branches use the nickname of its master branch unless it is set
3914
_see_also = ['info']
3915
takes_args = ['nickname?']
3916
takes_options = ['directory']
3917
def run(self, nickname=None, directory=u'.'):
3918
branch = Branch.open_containing(directory)[0]
3919
if nickname is None:
3920
self.printme(branch)
3922
branch.nick = nickname
3925
def printme(self, branch):
3926
self.outf.write('%s\n' % branch.nick)
3929
class cmd_alias(Command):
3930
__doc__ = """Set/unset and display aliases.
3933
Show the current aliases::
3937
Show the alias specified for 'll'::
3941
Set an alias for 'll'::
3943
brz alias ll="log --line -r-10..-1"
3945
To remove an alias for 'll'::
3947
brz alias --remove ll
3950
takes_args = ['name?']
3952
Option('remove', help='Remove the alias.'),
3955
def run(self, name=None, remove=False):
3957
self.remove_alias(name)
3959
self.print_aliases()
3961
equal_pos = name.find('=')
3963
self.print_alias(name)
3965
self.set_alias(name[:equal_pos], name[equal_pos+1:])
3967
def remove_alias(self, alias_name):
3968
if alias_name is None:
3969
raise errors.BzrCommandError(gettext(
3970
'brz alias --remove expects an alias to remove.'))
3971
# If alias is not found, print something like:
3972
# unalias: foo: not found
3973
c = _mod_config.GlobalConfig()
3974
c.unset_alias(alias_name)
3977
def print_aliases(self):
3978
"""Print out the defined aliases in a similar format to bash."""
3979
aliases = _mod_config.GlobalConfig().get_aliases()
3980
for key, value in sorted(viewitems(aliases)):
3981
self.outf.write('brz alias %s="%s"\n' % (key, value))
3984
def print_alias(self, alias_name):
3985
from .commands import get_alias
3986
alias = get_alias(alias_name)
3988
self.outf.write("brz alias: %s: not found\n" % alias_name)
3991
'brz alias %s="%s"\n' % (alias_name, ' '.join(alias)))
3993
def set_alias(self, alias_name, alias_command):
3994
"""Save the alias in the global config."""
3995
c = _mod_config.GlobalConfig()
3996
c.set_alias(alias_name, alias_command)
3999
class cmd_selftest(Command):
4000
__doc__ = """Run internal test suite.
4002
If arguments are given, they are regular expressions that say which tests
4003
should run. Tests matching any expression are run, and other tests are
4006
Alternatively if --first is given, matching tests are run first and then
4007
all other tests are run. This is useful if you have been working in a
4008
particular area, but want to make sure nothing else was broken.
4010
If --exclude is given, tests that match that regular expression are
4011
excluded, regardless of whether they match --first or not.
4013
To help catch accidential dependencies between tests, the --randomize
4014
option is useful. In most cases, the argument used is the word 'now'.
4015
Note that the seed used for the random number generator is displayed
4016
when this option is used. The seed can be explicitly passed as the
4017
argument to this option if required. This enables reproduction of the
4018
actual ordering used if and when an order sensitive problem is encountered.
4020
If --list-only is given, the tests that would be run are listed. This is
4021
useful when combined with --first, --exclude and/or --randomize to
4022
understand their impact. The test harness reports "Listed nn tests in ..."
4023
instead of "Ran nn tests in ..." when list mode is enabled.
4025
If the global option '--no-plugins' is given, plugins are not loaded
4026
before running the selftests. This has two effects: features provided or
4027
modified by plugins will not be tested, and tests provided by plugins will
4030
Tests that need working space on disk use a common temporary directory,
4031
typically inside $TMPDIR or /tmp.
4033
If you set BRZ_TEST_PDB=1 when running selftest, failing tests will drop
4034
into a pdb postmortem session.
4036
The --coverage=DIRNAME global option produces a report with covered code
4040
Run only tests relating to 'ignore'::
4044
Disable plugins and list tests as they're run::
4046
brz --no-plugins selftest -v
4048
# NB: this is used from the class without creating an instance, which is
4049
# why it does not have a self parameter.
4050
def get_transport_type(typestring):
4051
"""Parse and return a transport specifier."""
4052
if typestring == "sftp":
4053
from .tests import stub_sftp
4054
return stub_sftp.SFTPAbsoluteServer
4055
elif typestring == "memory":
4056
from .tests import test_server
4057
return memory.MemoryServer
4058
elif typestring == "fakenfs":
4059
from .tests import test_server
4060
return test_server.FakeNFSServer
4061
msg = "No known transport type %s. Supported types are: sftp\n" %\
4063
raise errors.BzrCommandError(msg)
4066
takes_args = ['testspecs*']
4067
takes_options = ['verbose',
4069
help='Stop when one test fails.',
4073
help='Use a different transport by default '
4074
'throughout the test suite.',
4075
type=get_transport_type),
4077
help='Run the benchmarks rather than selftests.',
4079
Option('lsprof-timed',
4080
help='Generate lsprof output for benchmarked'
4081
' sections of code.'),
4082
Option('lsprof-tests',
4083
help='Generate lsprof output for each test.'),
4085
help='Run all tests, but run specified tests first.',
4089
help='List the tests instead of running them.'),
4090
RegistryOption('parallel',
4091
help="Run the test suite in parallel.",
4092
lazy_registry=('breezy.tests', 'parallel_registry'),
4093
value_switches=False,
4095
Option('randomize', type=text_type, argname="SEED",
4096
help='Randomize the order of tests using the given'
4097
' seed or "now" for the current time.'),
4098
ListOption('exclude', type=text_type, argname="PATTERN",
4100
help='Exclude tests that match this regular'
4103
help='Output test progress via subunit v1.'),
4105
help='Output test progress via subunit v2.'),
4106
Option('strict', help='Fail on missing dependencies or '
4108
Option('load-list', type=text_type, argname='TESTLISTFILE',
4109
help='Load a test id list from a text file.'),
4110
ListOption('debugflag', type=text_type, short_name='E',
4111
help='Turn on a selftest debug flag.'),
4112
ListOption('starting-with', type=text_type, argname='TESTID',
4113
param_name='starting_with', short_name='s',
4115
'Load only the tests starting with TESTID.'),
4117
help="By default we disable fsync and fdatasync"
4118
" while running the test suite.")
4120
encoding_type = 'replace'
4123
Command.__init__(self)
4124
self.additional_selftest_args = {}
4126
def run(self, testspecs_list=None, verbose=False, one=False,
4127
transport=None, benchmark=None,
4129
first=False, list_only=False,
4130
randomize=None, exclude=None, strict=False,
4131
load_list=None, debugflag=None, starting_with=None, subunit1=False,
4132
subunit2=False, parallel=None, lsprof_tests=False, sync=False):
4134
# During selftest, disallow proxying, as it can cause severe
4135
# performance penalties and is only needed for thread
4136
# safety. The selftest command is assumed to not use threads
4137
# too heavily. The call should be as early as possible, as
4138
# error reporting for past duplicate imports won't have useful
4140
if sys.version_info[0] < 3:
4141
# TODO(pad.lv/1696545): Allow proxying on Python 3, since
4142
# disallowing it currently leads to failures in many places.
4143
lazy_import.disallow_proxying()
4148
raise errors.BzrCommandError("tests not available. Install the "
4149
"breezy tests to run the breezy testsuite.")
4151
if testspecs_list is not None:
4152
pattern = '|'.join(testspecs_list)
4157
from .tests import SubUnitBzrRunnerv1
4159
raise errors.BzrCommandError(gettext(
4160
"subunit not available. subunit needs to be installed "
4161
"to use --subunit."))
4162
self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
4163
# On Windows, disable automatic conversion of '\n' to '\r\n' in
4164
# stdout, which would corrupt the subunit stream.
4165
# FIXME: This has been fixed in subunit trunk (>0.0.5) so the
4166
# following code can be deleted when it's sufficiently deployed
4167
# -- vila/mgz 20100514
4168
if (sys.platform == "win32"
4169
and getattr(sys.stdout, 'fileno', None) is not None):
4171
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
4174
from .tests import SubUnitBzrRunnerv2
4176
raise errors.BzrCommandError(gettext(
4177
"subunit not available. subunit "
4178
"needs to be installed to use --subunit2."))
4179
self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv2
4182
self.additional_selftest_args.setdefault(
4183
'suite_decorators', []).append(parallel)
4185
raise errors.BzrCommandError(gettext(
4186
"--benchmark is no longer supported from brz 2.2; "
4187
"use bzr-usertest instead"))
4188
test_suite_factory = None
4190
exclude_pattern = None
4192
exclude_pattern = '(' + '|'.join(exclude) + ')'
4194
self._disable_fsync()
4195
selftest_kwargs = {"verbose": verbose,
4197
"stop_on_failure": one,
4198
"transport": transport,
4199
"test_suite_factory": test_suite_factory,
4200
"lsprof_timed": lsprof_timed,
4201
"lsprof_tests": lsprof_tests,
4202
"matching_tests_first": first,
4203
"list_only": list_only,
4204
"random_seed": randomize,
4205
"exclude_pattern": exclude_pattern,
4207
"load_list": load_list,
4208
"debug_flags": debugflag,
4209
"starting_with": starting_with
4211
selftest_kwargs.update(self.additional_selftest_args)
4213
# Make deprecation warnings visible, unless -Werror is set
4214
cleanup = symbol_versioning.activate_deprecation_warnings(
4217
result = tests.selftest(**selftest_kwargs)
4220
return int(not result)
4222
def _disable_fsync(self):
4223
"""Change the 'os' functionality to not synchronize."""
4224
self._orig_fsync = getattr(os, 'fsync', None)
4225
if self._orig_fsync is not None:
4226
os.fsync = lambda filedes: None
4227
self._orig_fdatasync = getattr(os, 'fdatasync', None)
4228
if self._orig_fdatasync is not None:
4229
os.fdatasync = lambda filedes: None
4232
class cmd_version(Command):
4233
__doc__ = """Show version of brz."""
4235
encoding_type = 'replace'
4237
Option("short", help="Print just the version number."),
4241
def run(self, short=False):
4242
from .version import show_version
4244
self.outf.write(breezy.version_string + '\n')
4246
show_version(to_file=self.outf)
4249
class cmd_rocks(Command):
4250
__doc__ = """Statement of optimism."""
4256
self.outf.write(gettext("It sure does!\n"))
4259
class cmd_find_merge_base(Command):
4260
__doc__ = """Find and print a base revision for merging two branches."""
4261
# TODO: Options to specify revisions on either side, as if
4262
# merging only part of the history.
4263
takes_args = ['branch', 'other']
4267
def run(self, branch, other):
4268
from .revision import ensure_null
4270
branch1 = Branch.open_containing(branch)[0]
4271
branch2 = Branch.open_containing(other)[0]
4272
self.add_cleanup(branch1.lock_read().unlock)
4273
self.add_cleanup(branch2.lock_read().unlock)
4274
last1 = ensure_null(branch1.last_revision())
4275
last2 = ensure_null(branch2.last_revision())
4277
graph = branch1.repository.get_graph(branch2.repository)
4278
base_rev_id = graph.find_unique_lca(last1, last2)
4280
self.outf.write(gettext('merge base is revision %s\n') % base_rev_id)
4283
class cmd_merge(Command):
4284
__doc__ = """Perform a three-way merge.
4286
The source of the merge can be specified either in the form of a branch,
4287
or in the form of a path to a file containing a merge directive generated
4288
with brz send. If neither is specified, the default is the upstream branch
4289
or the branch most recently merged using --remember. The source of the
4290
merge may also be specified in the form of a path to a file in another
4291
branch: in this case, only the modifications to that file are merged into
4292
the current working tree.
4294
When merging from a branch, by default brz will try to merge in all new
4295
work from the other branch, automatically determining an appropriate base
4296
revision. If this fails, you may need to give an explicit base.
4298
To pick a different ending revision, pass "--revision OTHER". brz will
4299
try to merge in all new work up to and including revision OTHER.
4301
If you specify two values, "--revision BASE..OTHER", only revisions BASE
4302
through OTHER, excluding BASE but including OTHER, will be merged. If this
4303
causes some revisions to be skipped, i.e. if the destination branch does
4304
not already contain revision BASE, such a merge is commonly referred to as
4305
a "cherrypick". Unlike a normal merge, Bazaar does not currently track
4306
cherrypicks. The changes look like a normal commit, and the history of the
4307
changes from the other branch is not stored in the commit.
4309
Revision numbers are always relative to the source branch.
4311
Merge will do its best to combine the changes in two branches, but there
4312
are some kinds of problems only a human can fix. When it encounters those,
4313
it will mark a conflict. A conflict means that you need to fix something,
4314
before you can commit.
4316
Use brz resolve when you have fixed a problem. See also brz conflicts.
4318
If there is no default branch set, the first merge will set it (use
4319
--no-remember to avoid setting it). After that, you can omit the branch
4320
to use the default. To change the default, use --remember. The value will
4321
only be saved if the remote location can be accessed.
4323
The results of the merge are placed into the destination working
4324
directory, where they can be reviewed (with brz diff), tested, and then
4325
committed to record the result of the merge.
4327
merge refuses to run if there are any uncommitted changes, unless
4328
--force is given. If --force is given, then the changes from the source
4329
will be merged with the current working tree, including any uncommitted
4330
changes in the tree. The --force option can also be used to create a
4331
merge revision which has more than two parents.
4333
If one would like to merge changes from the working tree of the other
4334
branch without merging any committed revisions, the --uncommitted option
4337
To select only some changes to merge, use "merge -i", which will prompt
4338
you to apply each diff hunk and file change, similar to "shelve".
4341
To merge all new revisions from brz.dev::
4343
brz merge ../brz.dev
4345
To merge changes up to and including revision 82 from brz.dev::
4347
brz merge -r 82 ../brz.dev
4349
To merge the changes introduced by 82, without previous changes::
4351
brz merge -r 81..82 ../brz.dev
4353
To apply a merge directive contained in /tmp/merge::
4355
brz merge /tmp/merge
4357
To create a merge revision with three parents from two branches
4358
feature1a and feature1b:
4360
brz merge ../feature1a
4361
brz merge ../feature1b --force
4362
brz commit -m 'revision with three parents'
4365
encoding_type = 'exact'
4366
_see_also = ['update', 'remerge', 'status-flags', 'send']
4367
takes_args = ['location?']
4372
help='Merge even if the destination tree has uncommitted changes.'),
4376
Option('show-base', help="Show base revision text in "
4378
Option('uncommitted', help='Apply uncommitted changes'
4379
' from a working copy, instead of branch changes.'),
4380
Option('pull', help='If the destination is already'
4381
' completely merged into the source, pull from the'
4382
' source rather than merging. When this happens,'
4383
' you do not need to commit the result.'),
4384
custom_help('directory',
4385
help='Branch to merge into, '
4386
'rather than the one containing the working directory.'),
4387
Option('preview', help='Instead of merging, show a diff of the'
4389
Option('interactive', help='Select changes interactively.',
4393
def run(self, location=None, revision=None, force=False,
4394
merge_type=None, show_base=False, reprocess=None, remember=None,
4395
uncommitted=False, pull=False,
4400
if merge_type is None:
4401
merge_type = _mod_merge.Merge3Merger
4403
if directory is None: directory = u'.'
4404
possible_transports = []
4406
allow_pending = True
4407
verified = 'inapplicable'
4409
tree = WorkingTree.open_containing(directory)[0]
4410
if tree.branch.revno() == 0:
4411
raise errors.BzrCommandError(gettext('Merging into empty branches not currently supported, '
4412
'https://bugs.launchpad.net/bzr/+bug/308562'))
4415
basis_tree = tree.revision_tree(tree.last_revision())
4416
except errors.NoSuchRevision:
4417
basis_tree = tree.basis_tree()
4419
# die as quickly as possible if there are uncommitted changes
4421
if tree.has_changes():
4422
raise errors.UncommittedChanges(tree)
4424
view_info = _get_view_info_for_change_reporter(tree)
4425
change_reporter = delta._ChangeReporter(
4426
unversioned_filter=tree.is_ignored, view_info=view_info)
4427
pb = ui.ui_factory.nested_progress_bar()
4428
self.add_cleanup(pb.finished)
4429
self.add_cleanup(tree.lock_write().unlock)
4430
if location is not None:
4432
mergeable = bundle.read_mergeable_from_url(location,
4433
possible_transports=possible_transports)
4434
except errors.NotABundle:
4438
raise errors.BzrCommandError(gettext('Cannot use --uncommitted'
4439
' with bundles or merge directives.'))
4441
if revision is not None:
4442
raise errors.BzrCommandError(gettext(
4443
'Cannot use -r with merge directives or bundles'))
4444
merger, verified = _mod_merge.Merger.from_mergeable(tree,
4447
if merger is None and uncommitted:
4448
if revision is not None and len(revision) > 0:
4449
raise errors.BzrCommandError(gettext('Cannot use --uncommitted and'
4450
' --revision at the same time.'))
4451
merger = self.get_merger_from_uncommitted(tree, location, None)
4452
allow_pending = False
4455
merger, allow_pending = self._get_merger_from_branch(tree,
4456
location, revision, remember, possible_transports, None)
4458
merger.merge_type = merge_type
4459
merger.reprocess = reprocess
4460
merger.show_base = show_base
4461
self.sanity_check_merger(merger)
4462
if (merger.base_rev_id == merger.other_rev_id and
4463
merger.other_rev_id is not None):
4464
# check if location is a nonexistent file (and not a branch) to
4465
# disambiguate the 'Nothing to do'
4466
if merger.interesting_files:
4467
if not merger.other_tree.has_filename(
4468
merger.interesting_files[0]):
4469
note(gettext("merger: ") + str(merger))
4470
raise errors.PathsDoNotExist([location])
4471
note(gettext('Nothing to do.'))
4473
if pull and not preview:
4474
if merger.interesting_files is not None:
4475
raise errors.BzrCommandError(gettext('Cannot pull individual files'))
4476
if (merger.base_rev_id == tree.last_revision()):
4477
result = tree.pull(merger.other_branch, False,
4478
merger.other_rev_id)
4479
result.report(self.outf)
4481
if merger.this_basis is None:
4482
raise errors.BzrCommandError(gettext(
4483
"This branch has no commits."
4484
" (perhaps you would prefer 'brz pull')"))
4486
return self._do_preview(merger)
4488
return self._do_interactive(merger)
4490
return self._do_merge(merger, change_reporter, allow_pending,
4493
def _get_preview(self, merger):
4494
tree_merger = merger.make_merger()
4495
tt = tree_merger.make_preview_transform()
4496
self.add_cleanup(tt.finalize)
4497
result_tree = tt.get_preview_tree()
4500
def _do_preview(self, merger):
4501
from .diff import show_diff_trees
4502
result_tree = self._get_preview(merger)
4503
path_encoding = osutils.get_diff_header_encoding()
4504
show_diff_trees(merger.this_tree, result_tree, self.outf,
4505
old_label='', new_label='',
4506
path_encoding=path_encoding)
4508
def _do_merge(self, merger, change_reporter, allow_pending, verified):
4509
merger.change_reporter = change_reporter
4510
conflict_count = merger.do_merge()
4512
merger.set_pending()
4513
if verified == 'failed':
4514
warning('Preview patch does not match changes')
4515
if conflict_count != 0:
4520
def _do_interactive(self, merger):
4521
"""Perform an interactive merge.
4523
This works by generating a preview tree of the merge, then using
4524
Shelver to selectively remove the differences between the working tree
4525
and the preview tree.
4527
from . import shelf_ui
4528
result_tree = self._get_preview(merger)
4529
writer = breezy.option.diff_writer_registry.get()
4530
shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
4531
reporter=shelf_ui.ApplyReporter(),
4532
diff_writer=writer(self.outf))
4538
def sanity_check_merger(self, merger):
4539
if (merger.show_base and
4540
not merger.merge_type is _mod_merge.Merge3Merger):
4541
raise errors.BzrCommandError(gettext("Show-base is not supported for this"
4542
" merge type. %s") % merger.merge_type)
4543
if merger.reprocess is None:
4544
if merger.show_base:
4545
merger.reprocess = False
4547
# Use reprocess if the merger supports it
4548
merger.reprocess = merger.merge_type.supports_reprocess
4549
if merger.reprocess and not merger.merge_type.supports_reprocess:
4550
raise errors.BzrCommandError(gettext("Conflict reduction is not supported"
4551
" for merge type %s.") %
4553
if merger.reprocess and merger.show_base:
4554
raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
4557
if (merger.merge_type.requires_file_merge_plan and
4558
(not getattr(merger.this_tree, 'plan_file_merge', None) or
4559
not getattr(merger.other_tree, 'plan_file_merge', None) or
4560
(merger.base_tree is not None and
4561
not getattr(merger.base_tree, 'plan_file_merge', None)))):
4562
raise errors.BzrCommandError(
4563
gettext('Plan file merge unsupported: '
4564
'Merge type incompatible with tree formats.'))
4566
def _get_merger_from_branch(self, tree, location, revision, remember,
4567
possible_transports, pb):
4568
"""Produce a merger from a location, assuming it refers to a branch."""
4569
# find the branch locations
4570
other_loc, user_location = self._select_branch_location(tree, location,
4572
if revision is not None and len(revision) == 2:
4573
base_loc, _unused = self._select_branch_location(tree,
4574
location, revision, 0)
4576
base_loc = other_loc
4578
other_branch, other_path = Branch.open_containing(other_loc,
4579
possible_transports)
4580
if base_loc == other_loc:
4581
base_branch = other_branch
4583
base_branch, base_path = Branch.open_containing(base_loc,
4584
possible_transports)
4585
# Find the revision ids
4586
other_revision_id = None
4587
base_revision_id = None
4588
if revision is not None:
4589
if len(revision) >= 1:
4590
other_revision_id = revision[-1].as_revision_id(other_branch)
4591
if len(revision) == 2:
4592
base_revision_id = revision[0].as_revision_id(base_branch)
4593
if other_revision_id is None:
4594
other_revision_id = _mod_revision.ensure_null(
4595
other_branch.last_revision())
4596
# Remember where we merge from. We need to remember if:
4597
# - user specify a location (and we don't merge from the parent
4599
# - user ask to remember or there is no previous location set to merge
4600
# from and user didn't ask to *not* remember
4601
if (user_location is not None
4603
or (remember is None
4604
and tree.branch.get_submit_branch() is None)))):
4605
tree.branch.set_submit_branch(other_branch.base)
4606
# Merge tags (but don't set them in the master branch yet, the user
4607
# might revert this merge). Commit will propagate them.
4608
other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
4609
merger = _mod_merge.Merger.from_revision_ids(tree,
4610
other_revision_id, base_revision_id, other_branch, base_branch)
4611
if other_path != '':
4612
allow_pending = False
4613
merger.interesting_files = [other_path]
4615
allow_pending = True
4616
return merger, allow_pending
4618
def get_merger_from_uncommitted(self, tree, location, pb):
4619
"""Get a merger for uncommitted changes.
4621
:param tree: The tree the merger should apply to.
4622
:param location: The location containing uncommitted changes.
4623
:param pb: The progress bar to use for showing progress.
4625
location = self._select_branch_location(tree, location)[0]
4626
other_tree, other_path = WorkingTree.open_containing(location)
4627
merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
4628
if other_path != '':
4629
merger.interesting_files = [other_path]
4632
def _select_branch_location(self, tree, user_location, revision=None,
4634
"""Select a branch location, according to possible inputs.
4636
If provided, branches from ``revision`` are preferred. (Both
4637
``revision`` and ``index`` must be supplied.)
4639
Otherwise, the ``location`` parameter is used. If it is None, then the
4640
``submit`` or ``parent`` location is used, and a note is printed.
4642
:param tree: The working tree to select a branch for merging into
4643
:param location: The location entered by the user
4644
:param revision: The revision parameter to the command
4645
:param index: The index to use for the revision parameter. Negative
4646
indices are permitted.
4647
:return: (selected_location, user_location). The default location
4648
will be the user-entered location.
4650
if (revision is not None and index is not None
4651
and revision[index] is not None):
4652
branch = revision[index].get_branch()
4653
if branch is not None:
4654
return branch, branch
4655
if user_location is None:
4656
location = self._get_remembered(tree, 'Merging from')
4658
location = user_location
4659
return location, user_location
4661
def _get_remembered(self, tree, verb_string):
4662
"""Use tree.branch's parent if none was supplied.
4664
Report if the remembered location was used.
4666
stored_location = tree.branch.get_submit_branch()
4667
stored_location_type = "submit"
4668
if stored_location is None:
4669
stored_location = tree.branch.get_parent()
4670
stored_location_type = "parent"
4671
mutter("%s", stored_location)
4672
if stored_location is None:
4673
raise errors.BzrCommandError(gettext("No location specified or remembered"))
4674
display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
4675
note(gettext("{0} remembered {1} location {2}").format(verb_string,
4676
stored_location_type, display_url))
4677
return stored_location
4680
class cmd_remerge(Command):
4681
__doc__ = """Redo a merge.
4683
Use this if you want to try a different merge technique while resolving
4684
conflicts. Some merge techniques are better than others, and remerge
4685
lets you try different ones on different files.
4687
The options for remerge have the same meaning and defaults as the ones for
4688
merge. The difference is that remerge can (only) be run when there is a
4689
pending merge, and it lets you specify particular files.
4692
Re-do the merge of all conflicted files, and show the base text in
4693
conflict regions, in addition to the usual THIS and OTHER texts::
4695
brz remerge --show-base
4697
Re-do the merge of "foobar", using the weave merge algorithm, with
4698
additional processing to reduce the size of conflict regions::
4700
brz remerge --merge-type weave --reprocess foobar
4702
takes_args = ['file*']
4707
help="Show base revision text in conflicts."),
4710
def run(self, file_list=None, merge_type=None, show_base=False,
4712
from .conflicts import restore
4713
if merge_type is None:
4714
merge_type = _mod_merge.Merge3Merger
4715
tree, file_list = WorkingTree.open_containing_paths(file_list)
4716
self.add_cleanup(tree.lock_write().unlock)
4717
parents = tree.get_parent_ids()
4718
if len(parents) != 2:
4719
raise errors.BzrCommandError(gettext("Sorry, remerge only works after normal"
4720
" merges. Not cherrypicking or"
4722
repository = tree.branch.repository
4723
interesting_files = None
4725
conflicts = tree.conflicts()
4726
if file_list is not None:
4727
interesting_files = set()
4728
for filename in file_list:
4729
if not tree.is_versioned(filename):
4730
raise errors.NotVersionedError(filename)
4731
interesting_files.add(filename)
4732
if tree.kind(filename) != "directory":
4735
for path, ie in tree.iter_entries_by_dir(specific_files=[filename]):
4736
interesting_files.add(path)
4737
new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4739
# Remerge only supports resolving contents conflicts
4740
allowed_conflicts = ('text conflict', 'contents conflict')
4741
restore_files = [c.path for c in conflicts
4742
if c.typestring in allowed_conflicts]
4743
_mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
4744
tree.set_conflicts(ConflictList(new_conflicts))
4745
if file_list is not None:
4746
restore_files = file_list
4747
for filename in restore_files:
4749
restore(tree.abspath(filename))
4750
except errors.NotConflicted:
4752
# Disable pending merges, because the file texts we are remerging
4753
# have not had those merges performed. If we use the wrong parents
4754
# list, we imply that the working tree text has seen and rejected
4755
# all the changes from the other tree, when in fact those changes
4756
# have not yet been seen.
4757
tree.set_parent_ids(parents[:1])
4759
merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
4760
merger.interesting_files = interesting_files
4761
merger.merge_type = merge_type
4762
merger.show_base = show_base
4763
merger.reprocess = reprocess
4764
conflicts = merger.do_merge()
4766
tree.set_parent_ids(parents)
4773
class cmd_revert(Command):
4775
Set files in the working tree back to the contents of a previous revision.
4777
Giving a list of files will revert only those files. Otherwise, all files
4778
will be reverted. If the revision is not specified with '--revision', the
4779
working tree basis revision is used. A revert operation affects only the
4780
working tree, not any revision history like the branch and repository or
4781
the working tree basis revision.
4783
To remove only some changes, without reverting to a prior version, use
4784
merge instead. For example, "merge . -r -2..-3" (don't forget the ".")
4785
will remove the changes introduced by the second last commit (-2), without
4786
affecting the changes introduced by the last commit (-1). To remove
4787
certain changes on a hunk-by-hunk basis, see the shelve command.
4788
To update the branch to a specific revision or the latest revision and
4789
update the working tree accordingly while preserving local changes, see the
4792
Uncommitted changes to files that are reverted will be discarded.
4793
Howver, by default, any files that have been manually changed will be
4794
backed up first. (Files changed only by merge are not backed up.) Backup
4795
files have '.~#~' appended to their name, where # is a number.
4797
When you provide files, you can use their current pathname or the pathname
4798
from the target revision. So you can use revert to "undelete" a file by
4799
name. If you name a directory, all the contents of that directory will be
4802
If you have newly added files since the target revision, they will be
4803
removed. If the files to be removed have been changed, backups will be
4804
created as above. Directories containing unknown files will not be
4807
The working tree contains a list of revisions that have been merged but
4808
not yet committed. These revisions will be included as additional parents
4809
of the next commit. Normally, using revert clears that list as well as
4810
reverting the files. If any files are specified, revert leaves the list
4811
of uncommitted merges alone and reverts only the files. Use ``brz revert
4812
.`` in the tree root to revert all files but keep the recorded merges,
4813
and ``brz revert --forget-merges`` to clear the pending merge list without
4814
reverting any files.
4816
Using "brz revert --forget-merges", it is possible to apply all of the
4817
changes from a branch in a single revision. To do this, perform the merge
4818
as desired. Then doing revert with the "--forget-merges" option will keep
4819
the content of the tree as it was, but it will clear the list of pending
4820
merges. The next commit will then contain all of the changes that are
4821
present in the other branch, but without any other parent revisions.
4822
Because this technique forgets where these changes originated, it may
4823
cause additional conflicts on later merges involving the same source and
4827
_see_also = ['cat', 'export', 'merge', 'shelve']
4830
Option('no-backup', "Do not save backups of reverted files."),
4831
Option('forget-merges',
4832
'Remove pending merge marker, without changing any files.'),
4834
takes_args = ['file*']
4836
def run(self, revision=None, no_backup=False, file_list=None,
4837
forget_merges=None):
4838
tree, file_list = WorkingTree.open_containing_paths(file_list)
4839
self.add_cleanup(tree.lock_tree_write().unlock)
4841
tree.set_parent_ids(tree.get_parent_ids()[:1])
4843
self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4846
def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4847
rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
4848
tree.revert(file_list, rev_tree, not no_backup, None,
4849
report_changes=True)
4852
class cmd_assert_fail(Command):
4853
__doc__ = """Test reporting of assertion failures"""
4854
# intended just for use in testing
4859
raise AssertionError("always fails")
4862
class cmd_help(Command):
4863
__doc__ = """Show help on a command or other topic.
4866
_see_also = ['topics']
4868
Option('long', 'Show help on all commands.'),
4870
takes_args = ['topic?']
4871
aliases = ['?', '--help', '-?', '-h']
4874
def run(self, topic=None, long=False):
4876
if topic is None and long:
4878
breezy.help.help(topic)
4881
class cmd_shell_complete(Command):
4882
__doc__ = """Show appropriate completions for context.
4884
For a list of all available commands, say 'brz shell-complete'.
4886
takes_args = ['context?']
4891
def run(self, context=None):
4892
from . import shellcomplete
4893
shellcomplete.shellcomplete(context)
4896
class cmd_missing(Command):
4897
__doc__ = """Show unmerged/unpulled revisions between two branches.
4899
OTHER_BRANCH may be local or remote.
4901
To filter on a range of revisions, you can use the command -r begin..end
4902
-r revision requests a specific revision, -r ..end or -r begin.. are
4906
1 - some missing revisions
4907
0 - no missing revisions
4911
Determine the missing revisions between this and the branch at the
4912
remembered pull location::
4916
Determine the missing revisions between this and another branch::
4918
brz missing http://server/branch
4920
Determine the missing revisions up to a specific revision on the other
4923
brz missing -r ..-10
4925
Determine the missing revisions up to a specific revision on this
4928
brz missing --my-revision ..-10
4931
_see_also = ['merge', 'pull']
4932
takes_args = ['other_branch?']
4935
Option('reverse', 'Reverse the order of revisions.'),
4937
'Display changes in the local branch only.'),
4938
Option('this', 'Same as --mine-only.'),
4939
Option('theirs-only',
4940
'Display changes in the remote branch only.'),
4941
Option('other', 'Same as --theirs-only.'),
4945
custom_help('revision',
4946
help='Filter on other branch revisions (inclusive). '
4947
'See "help revisionspec" for details.'),
4948
Option('my-revision',
4949
type=_parse_revision_str,
4950
help='Filter on local branch revisions (inclusive). '
4951
'See "help revisionspec" for details.'),
4952
Option('include-merged',
4953
'Show all revisions in addition to the mainline ones.'),
4954
Option('include-merges', hidden=True,
4955
help='Historical alias for --include-merged.'),
4957
encoding_type = 'replace'
4960
def run(self, other_branch=None, reverse=False, mine_only=False,
4962
log_format=None, long=False, short=False, line=False,
4963
show_ids=False, verbose=False, this=False, other=False,
4964
include_merged=None, revision=None, my_revision=None,
4966
from breezy.missing import find_unmerged, iter_log_revisions
4971
if include_merged is None:
4972
include_merged = False
4977
# TODO: We should probably check that we don't have mine-only and
4978
# theirs-only set, but it gets complicated because we also have
4979
# this and other which could be used.
4986
local_branch = Branch.open_containing(directory)[0]
4987
self.add_cleanup(local_branch.lock_read().unlock)
4989
parent = local_branch.get_parent()
4990
if other_branch is None:
4991
other_branch = parent
4992
if other_branch is None:
4993
raise errors.BzrCommandError(gettext("No peer location known"
4995
display_url = urlutils.unescape_for_display(parent,
4997
message(gettext("Using saved parent location: {0}\n").format(
5000
remote_branch = Branch.open(other_branch)
5001
if remote_branch.base == local_branch.base:
5002
remote_branch = local_branch
5004
self.add_cleanup(remote_branch.lock_read().unlock)
5006
local_revid_range = _revision_range_to_revid_range(
5007
_get_revision_range(my_revision, local_branch,
5010
remote_revid_range = _revision_range_to_revid_range(
5011
_get_revision_range(revision,
5012
remote_branch, self.name()))
5014
local_extra, remote_extra = find_unmerged(
5015
local_branch, remote_branch, restrict,
5016
backward=not reverse,
5017
include_merged=include_merged,
5018
local_revid_range=local_revid_range,
5019
remote_revid_range=remote_revid_range)
5021
if log_format is None:
5022
registry = log.log_formatter_registry
5023
log_format = registry.get_default(local_branch)
5024
lf = log_format(to_file=self.outf,
5026
show_timezone='original')
5029
if local_extra and not theirs_only:
5030
message(ngettext("You have %d extra revision:\n",
5031
"You have %d extra revisions:\n",
5035
if local_branch.supports_tags():
5036
rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
5037
for revision in iter_log_revisions(local_extra,
5038
local_branch.repository,
5041
lf.log_revision(revision)
5042
printed_local = True
5045
printed_local = False
5047
if remote_extra and not mine_only:
5048
if printed_local is True:
5050
message(ngettext("You are missing %d revision:\n",
5051
"You are missing %d revisions:\n",
5052
len(remote_extra)) %
5054
if remote_branch.supports_tags():
5055
rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
5056
for revision in iter_log_revisions(remote_extra,
5057
remote_branch.repository,
5060
lf.log_revision(revision)
5063
if mine_only and not local_extra:
5064
# We checked local, and found nothing extra
5065
message(gettext('This branch has no new revisions.\n'))
5066
elif theirs_only and not remote_extra:
5067
# We checked remote, and found nothing extra
5068
message(gettext('Other branch has no new revisions.\n'))
5069
elif not (mine_only or theirs_only or local_extra or
5071
# We checked both branches, and neither one had extra
5073
message(gettext("Branches are up to date.\n"))
5075
if not status_code and parent is None and other_branch is not None:
5076
self.add_cleanup(local_branch.lock_write().unlock)
5077
# handle race conditions - a parent might be set while we run.
5078
if local_branch.get_parent() is None:
5079
local_branch.set_parent(remote_branch.base)
5083
class cmd_pack(Command):
5084
__doc__ = """Compress the data within a repository.
5086
This operation compresses the data within a bazaar repository. As
5087
bazaar supports automatic packing of repository, this operation is
5088
normally not required to be done manually.
5090
During the pack operation, bazaar takes a backup of existing repository
5091
data, i.e. pack files. This backup is eventually removed by bazaar
5092
automatically when it is safe to do so. To save disk space by removing
5093
the backed up pack files, the --clean-obsolete-packs option may be
5096
Warning: If you use --clean-obsolete-packs and your machine crashes
5097
during or immediately after repacking, you may be left with a state
5098
where the deletion has been written to disk but the new packs have not
5099
been. In this case the repository may be unusable.
5102
_see_also = ['repositories']
5103
takes_args = ['branch_or_repo?']
5105
Option('clean-obsolete-packs', 'Delete obsolete packs to save disk space.'),
5108
def run(self, branch_or_repo='.', clean_obsolete_packs=False):
5109
dir = controldir.ControlDir.open_containing(branch_or_repo)[0]
5111
branch = dir.open_branch()
5112
repository = branch.repository
5113
except errors.NotBranchError:
5114
repository = dir.open_repository()
5115
repository.pack(clean_obsolete_packs=clean_obsolete_packs)
5118
class cmd_plugins(Command):
5119
__doc__ = """List the installed plugins.
5121
This command displays the list of installed plugins including
5122
version of plugin and a short description of each.
5124
--verbose shows the path where each plugin is located.
5126
A plugin is an external component for Bazaar that extends the
5127
revision control system, by adding or replacing code in Bazaar.
5128
Plugins can do a variety of things, including overriding commands,
5129
adding new commands, providing additional network transports and
5130
customizing log output.
5132
See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
5133
for further information on plugins including where to find them and how to
5134
install them. Instructions are also provided there on how to write new
5135
plugins using the Python programming language.
5137
takes_options = ['verbose']
5140
def run(self, verbose=False):
5141
from . import plugin
5142
# Don't give writelines a generator as some codecs don't like that
5143
self.outf.writelines(
5144
list(plugin.describe_plugins(show_paths=verbose)))
5147
class cmd_testament(Command):
5148
__doc__ = """Show testament (signing-form) of a revision."""
5151
Option('long', help='Produce long-format testament.'),
5153
help='Produce a strict-format testament.')]
5154
takes_args = ['branch?']
5155
encoding_type = 'exact'
5157
def run(self, branch=u'.', revision=None, long=False, strict=False):
5158
from .testament import Testament, StrictTestament
5160
testament_class = StrictTestament
5162
testament_class = Testament
5164
b = Branch.open_containing(branch)[0]
5166
b = Branch.open(branch)
5167
self.add_cleanup(b.lock_read().unlock)
5168
if revision is None:
5169
rev_id = b.last_revision()
5171
rev_id = revision[0].as_revision_id(b)
5172
t = testament_class.from_revision(b.repository, rev_id)
5174
self.outf.writelines(t.as_text_lines())
5176
self.outf.write(t.as_short_text())
5179
class cmd_annotate(Command):
5180
__doc__ = """Show the origin of each line in a file.
5182
This prints out the given file with an annotation on the left side
5183
indicating which revision, author and date introduced the change.
5185
If the origin is the same for a run of consecutive lines, it is
5186
shown only at the top, unless the --all option is given.
5188
# TODO: annotate directories; showing when each file was last changed
5189
# TODO: if the working copy is modified, show annotations on that
5190
# with new uncommitted lines marked
5191
aliases = ['ann', 'blame', 'praise']
5192
takes_args = ['filename']
5193
takes_options = [Option('all', help='Show annotations on all lines.'),
5194
Option('long', help='Show commit date in annotations.'),
5199
encoding_type = 'exact'
5202
def run(self, filename, all=False, long=False, revision=None,
5203
show_ids=False, directory=None):
5204
from .annotate import (
5207
wt, branch, relpath = \
5208
_open_directory_or_containing_tree_or_branch(filename, directory)
5210
self.add_cleanup(wt.lock_read().unlock)
5212
self.add_cleanup(branch.lock_read().unlock)
5213
tree = _get_one_revision_tree('annotate', revision, branch=branch)
5214
self.add_cleanup(tree.lock_read().unlock)
5215
if wt is not None and revision is None:
5216
file_id = wt.path2id(relpath)
5218
file_id = tree.path2id(relpath)
5220
raise errors.NotVersionedError(filename)
5221
if wt is not None and revision is None:
5222
# If there is a tree and we're not annotating historical
5223
# versions, annotate the working tree's content.
5224
annotate_file_tree(wt, relpath, self.outf, long, all,
5225
show_ids=show_ids, file_id=file_id)
5227
annotate_file_tree(tree, relpath, self.outf, long, all,
5228
show_ids=show_ids, branch=branch, file_id=file_id)
5231
class cmd_re_sign(Command):
5232
__doc__ = """Create a digital signature for an existing revision."""
5233
# TODO be able to replace existing ones.
5235
hidden = True # is this right ?
5236
takes_args = ['revision_id*']
5237
takes_options = ['directory', 'revision']
5239
def run(self, revision_id_list=None, revision=None, directory=u'.'):
5240
if revision_id_list is not None and revision is not None:
5241
raise errors.BzrCommandError(gettext('You can only supply one of revision_id or --revision'))
5242
if revision_id_list is None and revision is None:
5243
raise errors.BzrCommandError(gettext('You must supply either --revision or a revision_id'))
5244
b = WorkingTree.open_containing(directory)[0].branch
5245
self.add_cleanup(b.lock_write().unlock)
5246
return self._run(b, revision_id_list, revision)
5248
def _run(self, b, revision_id_list, revision):
5249
gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
5250
if revision_id_list is not None:
5251
b.repository.start_write_group()
5253
for revision_id in revision_id_list:
5254
revision_id = cache_utf8.encode(revision_id)
5255
b.repository.sign_revision(revision_id, gpg_strategy)
5257
b.repository.abort_write_group()
5260
b.repository.commit_write_group()
5261
elif revision is not None:
5262
if len(revision) == 1:
5263
revno, rev_id = revision[0].in_history(b)
5264
b.repository.start_write_group()
5266
b.repository.sign_revision(rev_id, gpg_strategy)
5268
b.repository.abort_write_group()
5271
b.repository.commit_write_group()
5272
elif len(revision) == 2:
5273
# are they both on rh- if so we can walk between them
5274
# might be nice to have a range helper for arbitrary
5275
# revision paths. hmm.
5276
from_revno, from_revid = revision[0].in_history(b)
5277
to_revno, to_revid = revision[1].in_history(b)
5278
if to_revid is None:
5279
to_revno = b.revno()
5280
if from_revno is None or to_revno is None:
5281
raise errors.BzrCommandError(gettext('Cannot sign a range of non-revision-history revisions'))
5282
b.repository.start_write_group()
5284
for revno in range(from_revno, to_revno + 1):
5285
b.repository.sign_revision(b.get_rev_id(revno),
5288
b.repository.abort_write_group()
5291
b.repository.commit_write_group()
5293
raise errors.BzrCommandError(gettext('Please supply either one revision, or a range.'))
5296
class cmd_bind(Command):
5297
__doc__ = """Convert the current branch into a checkout of the supplied branch.
5298
If no branch is supplied, rebind to the last bound location.
5300
Once converted into a checkout, commits must succeed on the master branch
5301
before they will be applied to the local branch.
5303
Bound branches use the nickname of its master branch unless it is set
5304
locally, in which case binding will update the local nickname to be
5308
_see_also = ['checkouts', 'unbind']
5309
takes_args = ['location?']
5310
takes_options = ['directory']
5312
def run(self, location=None, directory=u'.'):
5313
b, relpath = Branch.open_containing(directory)
5314
if location is None:
5316
location = b.get_old_bound_location()
5317
except errors.UpgradeRequired:
5318
raise errors.BzrCommandError(gettext('No location supplied. '
5319
'This format does not remember old locations.'))
5321
if location is None:
5322
if b.get_bound_location() is not None:
5323
raise errors.BzrCommandError(
5324
gettext('Branch is already bound'))
5326
raise errors.BzrCommandError(
5327
gettext('No location supplied'
5328
' and no previous location known'))
5329
b_other = Branch.open(location)
5332
except errors.DivergedBranches:
5333
raise errors.BzrCommandError(gettext('These branches have diverged.'
5334
' Try merging, and then bind again.'))
5335
if b.get_config().has_explicit_nickname():
5336
b.nick = b_other.nick
5339
class cmd_unbind(Command):
5340
__doc__ = """Convert the current checkout into a regular branch.
5342
After unbinding, the local branch is considered independent and subsequent
5343
commits will be local only.
5346
_see_also = ['checkouts', 'bind']
5348
takes_options = ['directory']
5350
def run(self, directory=u'.'):
5351
b, relpath = Branch.open_containing(directory)
5353
raise errors.BzrCommandError(gettext('Local branch is not bound'))
5356
class cmd_uncommit(Command):
5357
__doc__ = """Remove the last committed revision.
5359
--verbose will print out what is being removed.
5360
--dry-run will go through all the motions, but not actually
5363
If --revision is specified, uncommit revisions to leave the branch at the
5364
specified revision. For example, "brz uncommit -r 15" will leave the
5365
branch at revision 15.
5367
Uncommit leaves the working tree ready for a new commit. The only change
5368
it may make is to restore any pending merges that were present before
5372
# TODO: jam 20060108 Add an option to allow uncommit to remove
5373
# unreferenced information in 'branch-as-repository' branches.
5374
# TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
5375
# information in shared branches as well.
5376
_see_also = ['commit']
5377
takes_options = ['verbose', 'revision',
5378
Option('dry-run', help='Don\'t actually make changes.'),
5379
Option('force', help='Say yes to all questions.'),
5381
help='Keep tags that point to removed revisions.'),
5383
help="Only remove the commits from the local branch"
5384
" when in a checkout."
5387
takes_args = ['location?']
5389
encoding_type = 'replace'
5391
def run(self, location=None, dry_run=False, verbose=False,
5392
revision=None, force=False, local=False, keep_tags=False):
5393
if location is None:
5395
control, relpath = controldir.ControlDir.open_containing(location)
5397
tree = control.open_workingtree()
5399
except (errors.NoWorkingTree, errors.NotLocalUrl):
5401
b = control.open_branch()
5403
if tree is not None:
5404
self.add_cleanup(tree.lock_write().unlock)
5406
self.add_cleanup(b.lock_write().unlock)
5407
return self._run(b, tree, dry_run, verbose, revision, force,
5410
def _run(self, b, tree, dry_run, verbose, revision, force, local,
5412
from .log import log_formatter, show_log
5413
from .uncommit import uncommit
5415
last_revno, last_rev_id = b.last_revision_info()
5418
if revision is None:
5420
rev_id = last_rev_id
5422
# 'brz uncommit -r 10' actually means uncommit
5423
# so that the final tree is at revno 10.
5424
# but breezy.uncommit.uncommit() actually uncommits
5425
# the revisions that are supplied.
5426
# So we need to offset it by one
5427
revno = revision[0].in_history(b).revno + 1
5428
if revno <= last_revno:
5429
rev_id = b.get_rev_id(revno)
5431
if rev_id is None or _mod_revision.is_null(rev_id):
5432
self.outf.write(gettext('No revisions to uncommit.\n'))
5435
lf = log_formatter('short',
5437
show_timezone='original')
5442
direction='forward',
5443
start_revision=revno,
5444
end_revision=last_revno)
5447
self.outf.write(gettext('Dry-run, pretending to remove'
5448
' the above revisions.\n'))
5450
self.outf.write(gettext('The above revision(s) will be removed.\n'))
5453
if not ui.ui_factory.confirm_action(
5454
gettext(u'Uncommit these revisions'),
5455
'breezy.builtins.uncommit',
5457
self.outf.write(gettext('Canceled\n'))
5460
mutter('Uncommitting from {%s} to {%s}',
5461
last_rev_id, rev_id)
5462
uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
5463
revno=revno, local=local, keep_tags=keep_tags)
5464
self.outf.write(gettext('You can restore the old tip by running:\n'
5465
' brz pull . -r revid:%s\n') % last_rev_id.decode('utf-8'))
5468
class cmd_break_lock(Command):
5469
__doc__ = """Break a dead lock.
5471
This command breaks a lock on a repository, branch, working directory or
5474
CAUTION: Locks should only be broken when you are sure that the process
5475
holding the lock has been stopped.
5477
You can get information on what locks are open via the 'brz info
5478
[location]' command.
5482
brz break-lock brz+ssh://example.com/brz/foo
5483
brz break-lock --conf ~/.bazaar
5486
takes_args = ['location?']
5489
help='LOCATION is the directory where the config lock is.'),
5491
help='Do not ask for confirmation before breaking the lock.'),
5494
def run(self, location=None, config=False, force=False):
5495
if location is None:
5498
ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
5500
{'breezy.lockdir.break': True})
5502
conf = _mod_config.LockableConfig(file_name=location)
5505
control, relpath = controldir.ControlDir.open_containing(location)
5507
control.break_lock()
5508
except NotImplementedError:
5512
class cmd_wait_until_signalled(Command):
5513
__doc__ = """Test helper for test_start_and_stop_brz_subprocess_send_signal.
5515
This just prints a line to signal when it is ready, then blocks on stdin.
5521
self.outf.write("running\n")
5523
sys.stdin.readline()
5526
class cmd_serve(Command):
5527
__doc__ = """Run the brz server."""
5529
aliases = ['server']
5533
help='Serve on stdin/out for use from inetd or sshd.'),
5534
RegistryOption('protocol',
5535
help="Protocol to serve.",
5536
lazy_registry=('breezy.transport', 'transport_server_registry'),
5537
value_switches=True),
5539
help='Listen for connections on nominated address.', type=text_type),
5541
help='Listen for connections on nominated port. Passing 0 as '
5542
'the port number will result in a dynamically allocated '
5543
'port. The default port depends on the protocol.',
5545
custom_help('directory',
5546
help='Serve contents of this directory.'),
5547
Option('allow-writes',
5548
help='By default the server is a readonly server. Supplying '
5549
'--allow-writes enables write access to the contents of '
5550
'the served directory and below. Note that ``brz serve`` '
5551
'does not perform authentication, so unless some form of '
5552
'external authentication is arranged supplying this '
5553
'option leads to global uncontrolled write access to your '
5556
Option('client-timeout', type=float,
5557
help='Override the default idle client timeout (5min).'),
5560
def run(self, listen=None, port=None, inet=False, directory=None,
5561
allow_writes=False, protocol=None, client_timeout=None):
5562
from . import transport
5563
if directory is None:
5564
directory = osutils.getcwd()
5565
if protocol is None:
5566
protocol = transport.transport_server_registry.get()
5567
url = transport.location_to_url(directory)
5568
if not allow_writes:
5569
url = 'readonly+' + url
5570
t = transport.get_transport_from_url(url)
5571
protocol(t, listen, port, inet, client_timeout)
5574
class cmd_join(Command):
5575
__doc__ = """Combine a tree into its containing tree.
5577
This command requires the target tree to be in a rich-root format.
5579
The TREE argument should be an independent tree, inside another tree, but
5580
not part of it. (Such trees can be produced by "brz split", but also by
5581
running "brz branch" with the target inside a tree.)
5583
The result is a combined tree, with the subtree no longer an independent
5584
part. This is marked as a merge of the subtree into the containing tree,
5585
and all history is preserved.
5588
_see_also = ['split']
5589
takes_args = ['tree']
5591
Option('reference', help='Join by reference.', hidden=True),
5594
def run(self, tree, reference=False):
5595
from breezy.mutabletree import BadReferenceTarget
5596
sub_tree = WorkingTree.open(tree)
5597
parent_dir = osutils.dirname(sub_tree.basedir)
5598
containing_tree = WorkingTree.open_containing(parent_dir)[0]
5599
repo = containing_tree.branch.repository
5600
if not repo.supports_rich_root():
5601
raise errors.BzrCommandError(gettext(
5602
"Can't join trees because %s doesn't support rich root data.\n"
5603
"You can use brz upgrade on the repository.")
5607
containing_tree.add_reference(sub_tree)
5608
except BadReferenceTarget as e:
5609
# XXX: Would be better to just raise a nicely printable
5610
# exception from the real origin. Also below. mbp 20070306
5611
raise errors.BzrCommandError(
5612
gettext("Cannot join {0}. {1}").format(tree, e.reason))
5615
containing_tree.subsume(sub_tree)
5616
except errors.BadSubsumeSource as e:
5617
raise errors.BzrCommandError(
5618
gettext("Cannot join {0}. {1}").format(tree, e.reason))
5621
class cmd_split(Command):
5622
__doc__ = """Split a subdirectory of a tree into a separate tree.
5624
This command will produce a target tree in a format that supports
5625
rich roots, like 'rich-root' or 'rich-root-pack'. These formats cannot be
5626
converted into earlier formats like 'dirstate-tags'.
5628
The TREE argument should be a subdirectory of a working tree. That
5629
subdirectory will be converted into an independent tree, with its own
5630
branch. Commits in the top-level tree will not apply to the new subtree.
5633
_see_also = ['join']
5634
takes_args = ['tree']
5636
def run(self, tree):
5637
containing_tree, subdir = WorkingTree.open_containing(tree)
5638
sub_id = containing_tree.path2id(subdir)
5640
raise errors.NotVersionedError(subdir)
5642
containing_tree.extract(subdir, sub_id)
5643
except errors.RootNotRich:
5644
raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
5647
class cmd_merge_directive(Command):
5648
__doc__ = """Generate a merge directive for auto-merge tools.
5650
A directive requests a merge to be performed, and also provides all the
5651
information necessary to do so. This means it must either include a
5652
revision bundle, or the location of a branch containing the desired
5655
A submit branch (the location to merge into) must be supplied the first
5656
time the command is issued. After it has been supplied once, it will
5657
be remembered as the default.
5659
A public branch is optional if a revision bundle is supplied, but required
5660
if --diff or --plain is specified. It will be remembered as the default
5661
after the first use.
5664
takes_args = ['submit_branch?', 'public_branch?']
5668
_see_also = ['send']
5672
RegistryOption.from_kwargs('patch-type',
5673
'The type of patch to include in the directive.',
5675
value_switches=True,
5677
bundle='Bazaar revision bundle (default).',
5678
diff='Normal unified diff.',
5679
plain='No patch, just directive.'),
5680
Option('sign', help='GPG-sign the directive.'), 'revision',
5681
Option('mail-to', type=text_type,
5682
help='Instead of printing the directive, email to this address.'),
5683
Option('message', type=text_type, short_name='m',
5684
help='Message to use when committing this merge.')
5687
encoding_type = 'exact'
5689
def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
5690
sign=False, revision=None, mail_to=None, message=None,
5692
from .revision import ensure_null, NULL_REVISION
5693
include_patch, include_bundle = {
5694
'plain': (False, False),
5695
'diff': (True, False),
5696
'bundle': (True, True),
5698
branch = Branch.open(directory)
5699
stored_submit_branch = branch.get_submit_branch()
5700
if submit_branch is None:
5701
submit_branch = stored_submit_branch
5703
if stored_submit_branch is None:
5704
branch.set_submit_branch(submit_branch)
5705
if submit_branch is None:
5706
submit_branch = branch.get_parent()
5707
if submit_branch is None:
5708
raise errors.BzrCommandError(gettext('No submit branch specified or known'))
5710
stored_public_branch = branch.get_public_branch()
5711
if public_branch is None:
5712
public_branch = stored_public_branch
5713
elif stored_public_branch is None:
5714
# FIXME: Should be done only if we succeed ? -- vila 2012-01-03
5715
branch.set_public_branch(public_branch)
5716
if not include_bundle and public_branch is None:
5717
raise errors.BzrCommandError(gettext('No public branch specified or'
5719
base_revision_id = None
5720
if revision is not None:
5721
if len(revision) > 2:
5722
raise errors.BzrCommandError(gettext('brz merge-directive takes '
5723
'at most two one revision identifiers'))
5724
revision_id = revision[-1].as_revision_id(branch)
5725
if len(revision) == 2:
5726
base_revision_id = revision[0].as_revision_id(branch)
5728
revision_id = branch.last_revision()
5729
revision_id = ensure_null(revision_id)
5730
if revision_id == NULL_REVISION:
5731
raise errors.BzrCommandError(gettext('No revisions to bundle.'))
5732
directive = merge_directive.MergeDirective2.from_objects(
5733
branch.repository, revision_id, time.time(),
5734
osutils.local_time_offset(), submit_branch,
5735
public_branch=public_branch, include_patch=include_patch,
5736
include_bundle=include_bundle, message=message,
5737
base_revision_id=base_revision_id)
5740
self.outf.write(directive.to_signed(branch))
5742
self.outf.writelines(directive.to_lines())
5744
message = directive.to_email(mail_to, branch, sign)
5745
s = SMTPConnection(branch.get_config_stack())
5746
s.send_email(message)
5749
class cmd_send(Command):
5750
__doc__ = """Mail or create a merge-directive for submitting changes.
5752
A merge directive provides many things needed for requesting merges:
5754
* A machine-readable description of the merge to perform
5756
* An optional patch that is a preview of the changes requested
5758
* An optional bundle of revision data, so that the changes can be applied
5759
directly from the merge directive, without retrieving data from a
5762
`brz send` creates a compact data set that, when applied using brz
5763
merge, has the same effect as merging from the source branch.
5765
By default the merge directive is self-contained and can be applied to any
5766
branch containing submit_branch in its ancestory without needing access to
5769
If --no-bundle is specified, then Bazaar doesn't send the contents of the
5770
revisions, but only a structured request to merge from the
5771
public_location. In that case the public_branch is needed and it must be
5772
up-to-date and accessible to the recipient. The public_branch is always
5773
included if known, so that people can check it later.
5775
The submit branch defaults to the parent of the source branch, but can be
5776
overridden. Both submit branch and public branch will be remembered in
5777
branch.conf the first time they are used for a particular branch. The
5778
source branch defaults to that containing the working directory, but can
5779
be changed using --from.
5781
Both the submit branch and the public branch follow the usual behavior with
5782
respect to --remember: If there is no default location set, the first send
5783
will set it (use --no-remember to avoid setting it). After that, you can
5784
omit the location to use the default. To change the default, use
5785
--remember. The value will only be saved if the location can be accessed.
5787
In order to calculate those changes, brz must analyse the submit branch.
5788
Therefore it is most efficient for the submit branch to be a local mirror.
5789
If a public location is known for the submit_branch, that location is used
5790
in the merge directive.
5792
The default behaviour is to send the merge directive by mail, unless -o is
5793
given, in which case it is sent to a file.
5795
Mail is sent using your preferred mail program. This should be transparent
5796
on Windows (it uses MAPI). On Unix, it requires the xdg-email utility.
5797
If the preferred client can't be found (or used), your editor will be used.
5799
To use a specific mail program, set the mail_client configuration option.
5800
(For Thunderbird 1.5, this works around some bugs.) Supported values for
5801
specific clients are "claws", "evolution", "kmail", "mail.app" (MacOS X's
5802
Mail.app), "mutt", and "thunderbird"; generic options are "default",
5803
"editor", "emacsclient", "mapi", and "xdg-email". Plugins may also add
5806
If mail is being sent, a to address is required. This can be supplied
5807
either on the commandline, by setting the submit_to configuration
5808
option in the branch itself or the child_submit_to configuration option
5809
in the submit branch.
5811
Two formats are currently supported: "4" uses revision bundle format 4 and
5812
merge directive format 2. It is significantly faster and smaller than
5813
older formats. It is compatible with Bazaar 0.19 and later. It is the
5814
default. "0.9" uses revision bundle format 0.9 and merge directive
5815
format 1. It is compatible with Bazaar 0.12 - 0.18.
5817
The merge directives created by brz send may be applied using brz merge or
5818
brz pull by specifying a file containing a merge directive as the location.
5820
brz send makes extensive use of public locations to map local locations into
5821
URLs that can be used by other people. See `brz help configuration` to
5822
set them, and use `brz info` to display them.
5825
encoding_type = 'exact'
5827
_see_also = ['merge', 'pull']
5829
takes_args = ['submit_branch?', 'public_branch?']
5833
help='Do not include a bundle in the merge directive.'),
5834
Option('no-patch', help='Do not include a preview patch in the merge'
5837
help='Remember submit and public branch.'),
5839
help='Branch to generate the submission from, '
5840
'rather than the one containing the working directory.',
5843
Option('output', short_name='o',
5844
help='Write merge directive to this file or directory; '
5845
'use - for stdout.',
5848
help='Refuse to send if there are uncommitted changes in'
5849
' the working tree, --no-strict disables the check.'),
5850
Option('mail-to', help='Mail the request to this address.',
5854
Option('body', help='Body for the email.', type=text_type),
5855
RegistryOption('format',
5856
help='Use the specified output format.',
5857
lazy_registry=('breezy.send', 'format_registry')),
5860
def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5861
no_patch=False, revision=None, remember=None, output=None,
5862
format=None, mail_to=None, message=None, body=None,
5863
strict=None, **kwargs):
5864
from .send import send
5865
return send(submit_branch, revision, public_branch, remember,
5866
format, no_bundle, no_patch, output,
5867
kwargs.get('from', '.'), mail_to, message, body,
5872
class cmd_bundle_revisions(cmd_send):
5873
__doc__ = """Create a merge-directive for submitting changes.
5875
A merge directive provides many things needed for requesting merges:
5877
* A machine-readable description of the merge to perform
5879
* An optional patch that is a preview of the changes requested
5881
* An optional bundle of revision data, so that the changes can be applied
5882
directly from the merge directive, without retrieving data from a
5885
If --no-bundle is specified, then public_branch is needed (and must be
5886
up-to-date), so that the receiver can perform the merge using the
5887
public_branch. The public_branch is always included if known, so that
5888
people can check it later.
5890
The submit branch defaults to the parent, but can be overridden. Both
5891
submit branch and public branch will be remembered if supplied.
5893
If a public_branch is known for the submit_branch, that public submit
5894
branch is used in the merge instructions. This means that a local mirror
5895
can be used as your actual submit branch, once you have set public_branch
5898
Two formats are currently supported: "4" uses revision bundle format 4 and
5899
merge directive format 2. It is significantly faster and smaller than
5900
older formats. It is compatible with Bazaar 0.19 and later. It is the
5901
default. "0.9" uses revision bundle format 0.9 and merge directive
5902
format 1. It is compatible with Bazaar 0.12 - 0.18.
5907
help='Do not include a bundle in the merge directive.'),
5908
Option('no-patch', help='Do not include a preview patch in the merge'
5911
help='Remember submit and public branch.'),
5913
help='Branch to generate the submission from, '
5914
'rather than the one containing the working directory.',
5917
Option('output', short_name='o', help='Write directive to this file.',
5920
help='Refuse to bundle revisions if there are uncommitted'
5921
' changes in the working tree, --no-strict disables the check.'),
5923
RegistryOption('format',
5924
help='Use the specified output format.',
5925
lazy_registry=('breezy.send', 'format_registry')),
5927
aliases = ['bundle']
5929
_see_also = ['send', 'merge']
5933
def run(self, submit_branch=None, public_branch=None, no_bundle=False,
5934
no_patch=False, revision=None, remember=False, output=None,
5935
format=None, strict=None, **kwargs):
5938
from .send import send
5939
return send(submit_branch, revision, public_branch, remember,
5940
format, no_bundle, no_patch, output,
5941
kwargs.get('from', '.'), None, None, None,
5942
self.outf, strict=strict)
5945
class cmd_tag(Command):
5946
__doc__ = """Create, remove or modify a tag naming a revision.
5948
Tags give human-meaningful names to revisions. Commands that take a -r
5949
(--revision) option can be given -rtag:X, where X is any previously
5952
Tags are stored in the branch. Tags are copied from one branch to another
5953
along when you branch, push, pull or merge.
5955
It is an error to give a tag name that already exists unless you pass
5956
--force, in which case the tag is moved to point to the new revision.
5958
To rename a tag (change the name but keep it on the same revsion), run ``brz
5959
tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
5961
If no tag name is specified it will be determined through the
5962
'automatic_tag_name' hook. This can e.g. be used to automatically tag
5963
upstream releases by reading configure.ac. See ``brz help hooks`` for
5967
_see_also = ['commit', 'tags']
5968
takes_args = ['tag_name?']
5971
help='Delete this tag rather than placing it.',
5973
custom_help('directory',
5974
help='Branch in which to place the tag.'),
5976
help='Replace existing tags.',
5981
def run(self, tag_name=None,
5987
branch, relpath = Branch.open_containing(directory)
5988
self.add_cleanup(branch.lock_write().unlock)
5990
if tag_name is None:
5991
raise errors.BzrCommandError(gettext("No tag specified to delete."))
5992
branch.tags.delete_tag(tag_name)
5993
note(gettext('Deleted tag %s.') % tag_name)
5996
if len(revision) != 1:
5997
raise errors.BzrCommandError(gettext(
5998
"Tags can only be placed on a single revision, "
6000
revision_id = revision[0].as_revision_id(branch)
6002
revision_id = branch.last_revision()
6003
if tag_name is None:
6004
tag_name = branch.automatic_tag_name(revision_id)
6005
if tag_name is None:
6006
raise errors.BzrCommandError(gettext(
6007
"Please specify a tag name."))
6009
existing_target = branch.tags.lookup_tag(tag_name)
6010
except errors.NoSuchTag:
6011
existing_target = None
6012
if not force and existing_target not in (None, revision_id):
6013
raise errors.TagAlreadyExists(tag_name)
6014
if existing_target == revision_id:
6015
note(gettext('Tag %s already exists for that revision.') % tag_name)
6017
branch.tags.set_tag(tag_name, revision_id)
6018
if existing_target is None:
6019
note(gettext('Created tag %s.') % tag_name)
6021
note(gettext('Updated tag %s.') % tag_name)
6024
class cmd_tags(Command):
6025
__doc__ = """List tags.
6027
This command shows a table of tag names and the revisions they reference.
6032
custom_help('directory',
6033
help='Branch whose tags should be displayed.'),
6034
RegistryOption('sort',
6035
'Sort tags by different criteria.', title='Sorting',
6036
lazy_registry=('breezy.tag', 'tag_sort_methods')
6043
def run(self, directory='.', sort=None, show_ids=False, revision=None):
6044
from .tag import tag_sort_methods
6045
branch, relpath = Branch.open_containing(directory)
6047
tags = list(viewitems(branch.tags.get_tag_dict()))
6051
self.add_cleanup(branch.lock_read().unlock)
6053
# Restrict to the specified range
6054
tags = self._tags_for_range(branch, revision)
6056
sort = tag_sort_methods.get()
6059
# [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
6060
for index, (tag, revid) in enumerate(tags):
6062
revno = branch.revision_id_to_dotted_revno(revid)
6063
if isinstance(revno, tuple):
6064
revno = '.'.join(map(str, revno))
6065
except (errors.NoSuchRevision,
6066
errors.GhostRevisionsHaveNoRevno,
6067
errors.UnsupportedOperation):
6068
# Bad tag data/merges can lead to tagged revisions
6069
# which are not in this branch. Fail gracefully ...
6071
tags[index] = (tag, revno)
6073
for tag, revspec in tags:
6074
self.outf.write('%-20s %s\n' % (tag, revspec))
6076
def _tags_for_range(self, branch, revision):
6078
rev1, rev2 = _get_revision_range(revision, branch, self.name())
6079
revid1, revid2 = rev1.rev_id, rev2.rev_id
6080
# _get_revision_range will always set revid2 if it's not specified.
6081
# If revid1 is None, it means we want to start from the branch
6082
# origin which is always a valid ancestor. If revid1 == revid2, the
6083
# ancestry check is useless.
6084
if revid1 and revid1 != revid2:
6085
# FIXME: We really want to use the same graph than
6086
# branch.iter_merge_sorted_revisions below, but this is not
6087
# easily available -- vila 2011-09-23
6088
if branch.repository.get_graph().is_ancestor(revid2, revid1):
6089
# We don't want to output anything in this case...
6091
# only show revisions between revid1 and revid2 (inclusive)
6092
tagged_revids = branch.tags.get_reverse_tag_dict()
6094
for r in branch.iter_merge_sorted_revisions(
6095
start_revision_id=revid2, stop_revision_id=revid1,
6096
stop_rule='include'):
6097
revid_tags = tagged_revids.get(r[0], None)
6099
found.extend([(tag, r[0]) for tag in revid_tags])
6103
class cmd_reconfigure(Command):
6104
__doc__ = """Reconfigure the type of a brz directory.
6106
A target configuration must be specified.
6108
For checkouts, the bind-to location will be auto-detected if not specified.
6109
The order of preference is
6110
1. For a lightweight checkout, the current bound location.
6111
2. For branches that used to be checkouts, the previously-bound location.
6112
3. The push location.
6113
4. The parent location.
6114
If none of these is available, --bind-to must be specified.
6117
_see_also = ['branches', 'checkouts', 'standalone-trees', 'working-trees']
6118
takes_args = ['location?']
6120
RegistryOption.from_kwargs(
6123
help='The relation between branch and tree.',
6124
value_switches=True, enum_switch=False,
6125
branch='Reconfigure to be an unbound branch with no working tree.',
6126
tree='Reconfigure to be an unbound branch with a working tree.',
6127
checkout='Reconfigure to be a bound branch with a working tree.',
6128
lightweight_checkout='Reconfigure to be a lightweight'
6129
' checkout (with no local history).',
6131
RegistryOption.from_kwargs(
6133
title='Repository type',
6134
help='Location fo the repository.',
6135
value_switches=True, enum_switch=False,
6136
standalone='Reconfigure to be a standalone branch '
6137
'(i.e. stop using shared repository).',
6138
use_shared='Reconfigure to use a shared repository.',
6140
RegistryOption.from_kwargs(
6142
title='Trees in Repository',
6143
help='Whether new branches in the repository have trees.',
6144
value_switches=True, enum_switch=False,
6145
with_trees='Reconfigure repository to create '
6146
'working trees on branches by default.',
6147
with_no_trees='Reconfigure repository to not create '
6148
'working trees on branches by default.'
6150
Option('bind-to', help='Branch to bind checkout to.', type=text_type),
6152
help='Perform reconfiguration even if local changes'
6154
Option('stacked-on',
6155
help='Reconfigure a branch to be stacked on another branch.',
6159
help='Reconfigure a branch to be unstacked. This '
6160
'may require copying substantial data into it.',
6164
def run(self, location=None, bind_to=None, force=False,
6165
tree_type=None, repository_type=None, repository_trees=None,
6166
stacked_on=None, unstacked=None):
6167
directory = controldir.ControlDir.open(location)
6168
if stacked_on and unstacked:
6169
raise errors.BzrCommandError(gettext("Can't use both --stacked-on and --unstacked"))
6170
elif stacked_on is not None:
6171
reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
6173
reconfigure.ReconfigureUnstacked().apply(directory)
6174
# At the moment you can use --stacked-on and a different
6175
# reconfiguration shape at the same time; there seems no good reason
6177
if (tree_type is None and
6178
repository_type is None and
6179
repository_trees is None):
6180
if stacked_on or unstacked:
6183
raise errors.BzrCommandError(gettext('No target configuration '
6185
reconfiguration = None
6186
if tree_type == 'branch':
6187
reconfiguration = reconfigure.Reconfigure.to_branch(directory)
6188
elif tree_type == 'tree':
6189
reconfiguration = reconfigure.Reconfigure.to_tree(directory)
6190
elif tree_type == 'checkout':
6191
reconfiguration = reconfigure.Reconfigure.to_checkout(
6193
elif tree_type == 'lightweight-checkout':
6194
reconfiguration = reconfigure.Reconfigure.to_lightweight_checkout(
6197
reconfiguration.apply(force)
6198
reconfiguration = None
6199
if repository_type == 'use-shared':
6200
reconfiguration = reconfigure.Reconfigure.to_use_shared(directory)
6201
elif repository_type == 'standalone':
6202
reconfiguration = reconfigure.Reconfigure.to_standalone(directory)
6204
reconfiguration.apply(force)
6205
reconfiguration = None
6206
if repository_trees == 'with-trees':
6207
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
6209
elif repository_trees == 'with-no-trees':
6210
reconfiguration = reconfigure.Reconfigure.set_repository_trees(
6213
reconfiguration.apply(force)
6214
reconfiguration = None
6217
class cmd_switch(Command):
6218
__doc__ = """Set the branch of a checkout and update.
6220
For lightweight checkouts, this changes the branch being referenced.
6221
For heavyweight checkouts, this checks that there are no local commits
6222
versus the current bound branch, then it makes the local branch a mirror
6223
of the new location and binds to it.
6225
In both cases, the working tree is updated and uncommitted changes
6226
are merged. The user can commit or revert these as they desire.
6228
Pending merges need to be committed or reverted before using switch.
6230
The path to the branch to switch to can be specified relative to the parent
6231
directory of the current branch. For example, if you are currently in a
6232
checkout of /path/to/branch, specifying 'newbranch' will find a branch at
6235
Bound branches use the nickname of its master branch unless it is set
6236
locally, in which case switching will update the local nickname to be
6240
takes_args = ['to_location?']
6241
takes_options = ['directory',
6243
help='Switch even if local commits will be lost.'),
6245
Option('create-branch', short_name='b',
6246
help='Create the target branch from this one before'
6247
' switching to it.'),
6249
help='Store and restore uncommitted changes in the'
6253
def run(self, to_location=None, force=False, create_branch=False,
6254
revision=None, directory=u'.', store=False):
6255
from . import switch
6256
tree_location = directory
6257
revision = _get_one_revision('switch', revision)
6258
possible_transports = []
6259
control_dir = controldir.ControlDir.open_containing(tree_location,
6260
possible_transports=possible_transports)[0]
6261
if to_location is None:
6262
if revision is None:
6263
raise errors.BzrCommandError(gettext('You must supply either a'
6264
' revision or a location'))
6265
to_location = tree_location
6267
branch = control_dir.open_branch(
6268
possible_transports=possible_transports)
6269
had_explicit_nick = branch.get_config().has_explicit_nickname()
6270
except errors.NotBranchError:
6272
had_explicit_nick = False
6275
raise errors.BzrCommandError(
6276
gettext('cannot create branch without source branch'))
6277
to_location = lookup_new_sibling_branch(control_dir, to_location,
6278
possible_transports=possible_transports)
6279
to_branch = branch.controldir.sprout(to_location,
6280
possible_transports=possible_transports,
6281
source_branch=branch).open_branch()
6284
to_branch = Branch.open(to_location,
6285
possible_transports=possible_transports)
6286
except errors.NotBranchError:
6287
to_branch = open_sibling_branch(control_dir, to_location,
6288
possible_transports=possible_transports)
6289
if revision is not None:
6290
revision = revision.as_revision_id(to_branch)
6292
switch.switch(control_dir, to_branch, force, revision_id=revision,
6293
store_uncommitted=store)
6294
except controldir.BranchReferenceLoop:
6295
raise errors.BzrCommandError(
6296
gettext('switching would create a branch reference loop. '
6297
'Use the "bzr up" command to switch to a '
6298
'different revision.'))
6299
if had_explicit_nick:
6300
branch = control_dir.open_branch() #get the new branch!
6301
branch.nick = to_branch.nick
6302
note(gettext('Switched to branch: %s'),
6303
urlutils.unescape_for_display(to_branch.base, 'utf-8'))
6307
class cmd_view(Command):
6308
__doc__ = """Manage filtered views.
6310
Views provide a mask over the tree so that users can focus on
6311
a subset of a tree when doing their work. After creating a view,
6312
commands that support a list of files - status, diff, commit, etc -
6313
effectively have that list of files implicitly given each time.
6314
An explicit list of files can still be given but those files
6315
must be within the current view.
6317
In most cases, a view has a short life-span: it is created to make
6318
a selected change and is deleted once that change is committed.
6319
At other times, you may wish to create one or more named views
6320
and switch between them.
6322
To disable the current view without deleting it, you can switch to
6323
the pseudo view called ``off``. This can be useful when you need
6324
to see the whole tree for an operation or two (e.g. merge) but
6325
want to switch back to your view after that.
6328
To define the current view::
6330
brz view file1 dir1 ...
6332
To list the current view::
6336
To delete the current view::
6340
To disable the current view without deleting it::
6342
brz view --switch off
6344
To define a named view and switch to it::
6346
brz view --name view-name file1 dir1 ...
6348
To list a named view::
6350
brz view --name view-name
6352
To delete a named view::
6354
brz view --name view-name --delete
6356
To switch to a named view::
6358
brz view --switch view-name
6360
To list all views defined::
6364
To delete all views::
6366
brz view --delete --all
6370
takes_args = ['file*']
6373
help='Apply list or delete action to all views.',
6376
help='Delete the view.',
6379
help='Name of the view to define, list or delete.',
6383
help='Name of the view to switch to.',
6388
def run(self, file_list,
6394
tree, file_list = WorkingTree.open_containing_paths(file_list,
6396
current_view, view_dict = tree.views.get_view_info()
6401
raise errors.BzrCommandError(gettext(
6402
"Both --delete and a file list specified"))
6404
raise errors.BzrCommandError(gettext(
6405
"Both --delete and --switch specified"))
6407
tree.views.set_view_info(None, {})
6408
self.outf.write(gettext("Deleted all views.\n"))
6410
raise errors.BzrCommandError(gettext("No current view to delete"))
6412
tree.views.delete_view(name)
6413
self.outf.write(gettext("Deleted '%s' view.\n") % name)
6416
raise errors.BzrCommandError(gettext(
6417
"Both --switch and a file list specified"))
6419
raise errors.BzrCommandError(gettext(
6420
"Both --switch and --all specified"))
6421
elif switch == 'off':
6422
if current_view is None:
6423
raise errors.BzrCommandError(gettext("No current view to disable"))
6424
tree.views.set_view_info(None, view_dict)
6425
self.outf.write(gettext("Disabled '%s' view.\n") % (current_view))
6427
tree.views.set_view_info(switch, view_dict)
6428
view_str = views.view_display_str(tree.views.lookup_view())
6429
self.outf.write(gettext("Using '{0}' view: {1}\n").format(switch, view_str))
6432
self.outf.write(gettext('Views defined:\n'))
6433
for view in sorted(view_dict):
6434
if view == current_view:
6438
view_str = views.view_display_str(view_dict[view])
6439
self.outf.write('%s %-20s %s\n' % (active, view, view_str))
6441
self.outf.write(gettext('No views defined.\n'))
6444
# No name given and no current view set
6447
raise errors.BzrCommandError(gettext(
6448
"Cannot change the 'off' pseudo view"))
6449
tree.views.set_view(name, sorted(file_list))
6450
view_str = views.view_display_str(tree.views.lookup_view())
6451
self.outf.write(gettext("Using '{0}' view: {1}\n").format(name, view_str))
6455
# No name given and no current view set
6456
self.outf.write(gettext('No current view.\n'))
6458
view_str = views.view_display_str(tree.views.lookup_view(name))
6459
self.outf.write(gettext("'{0}' view is: {1}\n").format(name, view_str))
6462
class cmd_hooks(Command):
6463
__doc__ = """Show hooks."""
6468
for hook_key in sorted(hooks.known_hooks.keys()):
6469
some_hooks = hooks.known_hooks_key_to_object(hook_key)
6470
self.outf.write("%s:\n" % type(some_hooks).__name__)
6471
for hook_name, hook_point in sorted(some_hooks.items()):
6472
self.outf.write(" %s:\n" % (hook_name,))
6473
found_hooks = list(hook_point)
6475
for hook in found_hooks:
6476
self.outf.write(" %s\n" %
6477
(some_hooks.get_hook_name(hook),))
6479
self.outf.write(gettext(" <no hooks installed>\n"))
6482
class cmd_remove_branch(Command):
6483
__doc__ = """Remove a branch.
6485
This will remove the branch from the specified location but
6486
will keep any working tree or repository in place.
6490
Remove the branch at repo/trunk::
6492
brz remove-branch repo/trunk
6496
takes_args = ["location?"]
6498
takes_options = ['directory',
6499
Option('force', help='Remove branch even if it is the active branch.')]
6501
aliases = ["rmbranch"]
6503
def run(self, directory=None, location=None, force=False):
6504
br = open_nearby_branch(near=directory, location=location)
6505
if not force and br.controldir.has_workingtree():
6507
active_branch = br.controldir.open_branch(name="")
6508
except errors.NotBranchError:
6509
active_branch = None
6510
if (active_branch is not None and
6511
br.control_url == active_branch.control_url):
6512
raise errors.BzrCommandError(
6513
gettext("Branch is active. Use --force to remove it."))
6514
br.controldir.destroy_branch(br.name)
6517
class cmd_shelve(Command):
6518
__doc__ = """Temporarily set aside some changes from the current tree.
6520
Shelve allows you to temporarily put changes you've made "on the shelf",
6521
ie. out of the way, until a later time when you can bring them back from
6522
the shelf with the 'unshelve' command. The changes are stored alongside
6523
your working tree, and so they aren't propagated along with your branch nor
6524
will they survive its deletion.
6526
If shelve --list is specified, previously-shelved changes are listed.
6528
Shelve is intended to help separate several sets of changes that have
6529
been inappropriately mingled. If you just want to get rid of all changes
6530
and you don't need to restore them later, use revert. If you want to
6531
shelve all text changes at once, use shelve --all.
6533
If filenames are specified, only the changes to those files will be
6534
shelved. Other files will be left untouched.
6536
If a revision is specified, changes since that revision will be shelved.
6538
You can put multiple items on the shelf, and by default, 'unshelve' will
6539
restore the most recently shelved changes.
6541
For complicated changes, it is possible to edit the changes in a separate
6542
editor program to decide what the file remaining in the working copy
6543
should look like. To do this, add the configuration option
6545
change_editor = PROGRAM @new_path @old_path
6547
where @new_path is replaced with the path of the new version of the
6548
file and @old_path is replaced with the path of the old version of
6549
the file. The PROGRAM should save the new file with the desired
6550
contents of the file in the working tree.
6554
takes_args = ['file*']
6559
Option('all', help='Shelve all changes.'),
6561
RegistryOption('writer', 'Method to use for writing diffs.',
6562
breezy.option.diff_writer_registry,
6563
value_switches=True, enum_switch=False),
6565
Option('list', help='List shelved changes.'),
6567
help='Destroy removed changes instead of shelving them.'),
6569
_see_also = ['unshelve', 'configuration']
6571
def run(self, revision=None, all=False, file_list=None, message=None,
6572
writer=None, list=False, destroy=False, directory=None):
6574
return self.run_for_list(directory=directory)
6575
from .shelf_ui import Shelver
6577
writer = breezy.option.diff_writer_registry.get()
6579
shelver = Shelver.from_args(writer(self.outf), revision, all,
6580
file_list, message, destroy=destroy, directory=directory)
6585
except errors.UserAbort:
6588
def run_for_list(self, directory=None):
6589
if directory is None:
6591
tree = WorkingTree.open_containing(directory)[0]
6592
self.add_cleanup(tree.lock_read().unlock)
6593
manager = tree.get_shelf_manager()
6594
shelves = manager.active_shelves()
6595
if len(shelves) == 0:
6596
note(gettext('No shelved changes.'))
6598
for shelf_id in reversed(shelves):
6599
message = manager.get_metadata(shelf_id).get(b'message')
6601
message = '<no message>'
6602
self.outf.write('%3d: %s\n' % (shelf_id, message))
6606
class cmd_unshelve(Command):
6607
__doc__ = """Restore shelved changes.
6609
By default, the most recently shelved changes are restored. However if you
6610
specify a shelf by id those changes will be restored instead. This works
6611
best when the changes don't depend on each other.
6614
takes_args = ['shelf_id?']
6617
RegistryOption.from_kwargs(
6618
'action', help="The action to perform.",
6619
enum_switch=False, value_switches=True,
6620
apply="Apply changes and remove from the shelf.",
6621
dry_run="Show changes, but do not apply or remove them.",
6622
preview="Instead of unshelving the changes, show the diff that "
6623
"would result from unshelving.",
6624
delete_only="Delete changes without applying them.",
6625
keep="Apply changes but don't delete them.",
6628
_see_also = ['shelve']
6630
def run(self, shelf_id=None, action='apply', directory=u'.'):
6631
from .shelf_ui import Unshelver
6632
unshelver = Unshelver.from_args(shelf_id, action, directory=directory)
6636
unshelver.tree.unlock()
6639
class cmd_clean_tree(Command):
6640
__doc__ = """Remove unwanted files from working tree.
6642
By default, only unknown files, not ignored files, are deleted. Versioned
6643
files are never deleted.
6645
Another class is 'detritus', which includes files emitted by brz during
6646
normal operations and selftests. (The value of these files decreases with
6649
If no options are specified, unknown files are deleted. Otherwise, option
6650
flags are respected, and may be combined.
6652
To check what clean-tree will do, use --dry-run.
6654
takes_options = ['directory',
6655
Option('ignored', help='Delete all ignored files.'),
6656
Option('detritus', help='Delete conflict files, merge and revert'
6657
' backups, and failed selftest dirs.'),
6659
help='Delete files unknown to brz (default).'),
6660
Option('dry-run', help='Show files to delete instead of'
6662
Option('force', help='Do not prompt before deleting.')]
6663
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
6664
force=False, directory=u'.'):
6665
from .clean_tree import clean_tree
6666
if not (unknown or ignored or detritus):
6670
clean_tree(directory, unknown=unknown, ignored=ignored,
6671
detritus=detritus, dry_run=dry_run, no_prompt=force)
6674
class cmd_reference(Command):
6675
__doc__ = """list, view and set branch locations for nested trees.
6677
If no arguments are provided, lists the branch locations for nested trees.
6678
If one argument is provided, display the branch location for that tree.
6679
If two arguments are provided, set the branch location for that tree.
6684
takes_args = ['path?', 'location?']
6686
def run(self, path=None, location=None):
6688
if path is not None:
6690
tree, branch, relpath =(
6691
controldir.ControlDir.open_containing_tree_or_branch(branchdir))
6692
if path is not None:
6695
tree = branch.basis_tree()
6697
info = viewitems(branch._get_all_reference_info())
6698
self._display_reference_info(tree, branch, info)
6700
if not tree.is_versioned(path):
6701
raise errors.NotVersionedError(path)
6702
if location is None:
6703
info = [(path, branch.get_reference_info(path))]
6704
self._display_reference_info(tree, branch, info)
6706
branch.set_reference_info(
6707
path, location, file_id=tree.path2id(path))
6709
def _display_reference_info(self, tree, branch, info):
6711
for path, (location, file_id) in info:
6712
ref_list.append((path, location))
6713
for path, location in sorted(ref_list):
6714
self.outf.write('%s %s\n' % (path, location))
6717
class cmd_export_pot(Command):
6718
__doc__ = """Export command helps and error messages in po format."""
6721
takes_options = [Option('plugin',
6722
help='Export help text from named command '\
6723
'(defaults to all built in commands).',
6725
Option('include-duplicates',
6726
help='Output multiple copies of the same msgid '
6727
'string if it appears more than once.'),
6730
def run(self, plugin=None, include_duplicates=False):
6731
from .export_pot import export_pot
6732
export_pot(self.outf, plugin, include_duplicates)
6735
class cmd_import(Command):
6736
__doc__ = """Import sources from a directory, tarball or zip file
6738
This command will import a directory, tarball or zip file into a bzr
6739
tree, replacing any versioned files already present. If a directory is
6740
specified, it is used as the target. If the directory does not exist, or
6741
is not versioned, it is created.
6743
Tarballs may be gzip or bzip2 compressed. This is autodetected.
6745
If the tarball or zip has a single root directory, that directory is
6746
stripped when extracting the tarball. This is not done for directories.
6749
takes_args = ['source', 'tree?']
6751
def run(self, source, tree=None):
6752
from .upstream_import import do_import
6753
do_import(source, tree)
6756
class cmd_link_tree(Command):
6757
__doc__ = """Hardlink matching files to another tree.
6759
Only files with identical content and execute bit will be linked.
6762
takes_args = ['location']
6764
def run(self, location):
6765
from .transform import link_tree
6766
target_tree = WorkingTree.open_containing(".")[0]
6767
source_tree = WorkingTree.open(location)
6768
with target_tree.lock_write(), source_tree.lock_read():
6769
link_tree(target_tree, source_tree)
6772
class cmd_fetch_ghosts(Command):
6773
__doc__ = """Attempt to retrieve ghosts from another branch.
6775
If the other branch is not supplied, the last-pulled branch is used.
6779
aliases = ['fetch-missing']
6780
takes_args = ['branch?']
6781
takes_options = [Option('no-fix', help="Skip additional synchonization.")]
6783
def run(self, branch=None, no_fix=False):
6784
from .fetch_ghosts import GhostFetcher
6785
installed, failed = GhostFetcher.from_cmdline(branch).run()
6786
if len(installed) > 0:
6787
self.outf.write("Installed:\n")
6788
for rev in installed:
6789
self.outf.write(rev.decode('utf-8') + "\n")
6791
self.outf.write("Still missing:\n")
6793
self.outf.write(rev.decode('utf-8') + "\n")
6794
if not no_fix and len(installed) > 0:
6795
cmd_reconcile().run(".")
6798
def _register_lazy_builtins():
6799
# register lazy builtins from other modules; called at startup and should
6800
# be only called once.
6801
for (name, aliases, module_name) in [
6802
('cmd_bisect', [], 'breezy.bisect'),
6803
('cmd_bundle_info', [], 'breezy.bundle.commands'),
6804
('cmd_config', [], 'breezy.config'),
6805
('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
6806
('cmd_version_info', [], 'breezy.cmd_version_info'),
6807
('cmd_resolve', ['resolved'], 'breezy.conflicts'),
6808
('cmd_conflicts', [], 'breezy.conflicts'),
6809
('cmd_ping', [], 'breezy.bzr.smart.ping'),
6810
('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
6811
('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
6812
('cmd_test_script', [], 'breezy.cmd_test_script'),
6814
builtin_command_registry.register_lazy(name, aliases, module_name)