3
Various useful plugins for working with bzr.
6
from errors import CommandError
7
from patchsource import BzrPatchSource
8
from shelf import Shelf
9
from switch import cmd_switch
13
from bzrlib import DEFAULT_IGNORE
14
import bzrlib.builtins
16
import bzrlib.commands
17
from bzrlib.commands import get_cmd_object
18
from bzrlib.errors import BzrCommandError
19
from bzrlib.help import command_usage
20
from bzrlib.option import Option
21
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
25
DEFAULT_IGNORE.append('./.shelf')
26
DEFAULT_IGNORE.append('./.bzr-shelf*')
29
class cmd_clean_tree(bzrlib.commands.Command):
30
"""Remove unwanted files from working tree.
32
By default, only unknown files, not ignored files, are deleted. Versioned
33
files are never deleted.
35
Another class is 'detritus', which includes files emitted by bzr during
36
normal operations and selftests. (The value of these files decreases with
39
If no options are specified, unknown files are deleted. Otherwise, option
40
flags are respected, and may be combined.
42
To check what clean-tree will do, use --dry-run.
44
takes_options = [Option('ignored', help='delete all ignored files.'),
45
Option('detritus', help='delete conflict files, merge'
46
' backups, and failed selftest dirs.'),
48
help='delete files unknown to bzr. (default)'),
49
Option('dry-run', help='show files to delete instead of'
51
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
52
from clean_tree import clean_tree
53
if not (unknown or ignored or detritus):
55
clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
58
class cmd_graph_ancestry(bzrlib.commands.Command):
59
"""Produce ancestry graphs using dot.
61
Output format is detected according to file extension. Some of the more
62
common output formats are html, png, gif, svg, ps. An extension of '.dot'
63
will cause a dot graph file to be produced. HTML output has mouseovers
64
that show the commit message.
66
Branches are labeled r?, where ? is the revno. If they have no revno,
67
with the last 5 characters of their revision identifier are used instead.
69
The value starting with d is "(maximum) distance from the null revision".
71
If --merge-branch is specified, the two branches are compared and a merge
79
blue COMMON non-history ancestor
80
green Merge base (COMMON ancestor farthest from the null revision)
81
dotted Ghost revision (missing from branch storage)
83
Ancestry is usually collapsed by skipping revisions with a single parent
84
and descendant. The number of skipped revisions is shown on the arrow.
85
This feature can be disabled with --no-collapse.
87
By default, revisions are ordered by distance from root, but they can be
88
clustered instead using --cluster.
90
If available, rsvg is used to antialias PNG and JPEG output, but this can
91
be disabled with --no-antialias.
93
takes_args = ['branch', 'file']
94
takes_options = [Option('no-collapse', help="Do not skip simple nodes"),
95
Option('no-antialias',
96
help="Do not use rsvg to produce antialiased output"),
97
Option('merge-branch', type=str,
98
help="Use this branch to calcuate a merge base"),
99
Option('cluster', help="Use clustered output.")]
100
def run(self, branch, file, no_collapse=False, no_antialias=False,
101
merge_branch=None, cluster=False):
107
graph.write_ancestry_file(branch, file, not no_collapse,
108
not no_antialias, merge_branch, ranking)
110
class cmd_fetch_ghosts(bzrlib.commands.Command):
111
"""Attempt to retrieve ghosts from another branch.
112
If the other branch is not supplied, the last-pulled branch is used.
114
aliases = ['fetch-missing']
115
takes_args = ['branch?']
116
takes_options = [Option('no-fix')]
117
def run(self, branch=None, no_fix=False):
118
from fetch_ghosts import fetch_ghosts
119
fetch_ghosts(branch, no_fix)
121
strip_help="""Strip the smallest prefix containing num leading slashes from \
122
each file name found in the patch file."""
123
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
124
help="""Handle extra bzr tags""")
125
class cmd_patch(bzrlib.commands.Command):
126
"""Apply a named patch to the current tree.
128
takes_args = ['filename?']
129
takes_options = [Option('strip', type=int, help=strip_help)]
130
def run(self, filename=None, strip=-1, bzrdiff=0):
131
from patch import patch
132
from bzrlib.workingtree import WorkingTree
133
wt = WorkingTree.open_containing('.')[0]
135
if bzrdiff: strip = 0
138
return patch(wt, filename, strip, legacy= not bzrdiff)
140
class cmd_shelve(bzrlib.commands.Command):
141
"""Temporarily set aside some changes from the current tree.
143
Shelve allows you to temporarily put changes you've made "on the shelf",
144
ie. out of the way, until a later time when you can bring them back from
145
the shelf with the 'unshelve' command.
147
Shelve is intended to help separate several sets of text changes that have
148
been inappropriately mingled. If you just want to get rid of all changes
149
(text and otherwise) and you don't need to restore them later, use revert.
150
If you want to shelve all text changes at once, use shelve --all.
152
By default shelve asks you what you want to shelve, press '?' at the
153
prompt to get help. To shelve everything run shelve --all.
155
If filenames are specified, only the changes to those files will be
156
shelved, other files will be left untouched.
158
If a revision is specified, changes since that revision will be shelved.
160
You can put multiple items on the shelf. Normally each time you run
161
unshelve the most recently shelved changes will be reinstated. However,
162
you can also unshelve changes in a different order by explicitly
163
specifiying which changes to unshelve. This works best when the changes
164
don't depend on each other.
167
takes_args = ['file*']
168
takes_options = ['message', 'revision',
169
Option('all', help='Shelve all changes without prompting')]
171
def run(self, all=False, file_list=None, message=None, revision=None):
172
if revision is not None and revision:
173
if len(revision) == 1:
174
revision = revision[0]
176
raise CommandError("shelve only accepts a single revision "
179
source = BzrPatchSource(revision, file_list)
180
s = Shelf(source.base)
181
s.shelve(source, all, message)
185
# The following classes are only used as subcommands for 'shelf', they're
186
# not to be registered directly with bzr.
188
class cmd_shelf_list(bzrlib.commands.Command):
189
"""List the patches on the current shelf."""
190
aliases = ['list', 'ls']
195
class cmd_shelf_delete(bzrlib.commands.Command):
196
"""Delete the patch from the current shelf."""
197
aliases = ['delete', 'del']
198
takes_args = ['patch']
199
def run(self, patch):
200
self.shelf.delete(patch)
203
class cmd_shelf_switch(bzrlib.commands.Command):
204
"""Switch to the other shelf, create it if necessary."""
206
takes_args = ['othershelf']
207
def run(self, othershelf):
208
s = Shelf(self.shelf.base, othershelf)
212
class cmd_shelf_show(bzrlib.commands.Command):
213
"""Show the contents of the specified or topmost patch."""
214
aliases = ['show', 'cat', 'display']
215
takes_args = ['patch?']
216
def run(self, patch=None):
217
self.shelf.display(patch)
220
class cmd_shelf_upgrade(bzrlib.commands.Command):
221
"""Upgrade old format shelves."""
222
aliases = ['upgrade']
227
class cmd_shelf(bzrlib.commands.Command):
228
"""Perform various operations on your shelved patches. See also shelve."""
229
takes_args = ['subcommand', 'args*']
231
subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
232
cmd_shelf_show, cmd_shelf_upgrade]
234
def run(self, subcommand, args_list):
237
cmd = self._get_cmd_object(subcommand)
238
source = BzrPatchSource()
239
s = Shelf(source.base)
241
return cmd.run_argv_aliases(args_list)
243
def _get_cmd_object(self, cmd_name):
244
for cmd_class in self.subcommands:
245
for alias in cmd_class.aliases:
246
if alias == cmd_name:
248
raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
251
text = ["%s\n\nSubcommands:\n" % self.__doc__]
253
for cmd_class in self.subcommands:
254
text.extend(self.sub_help(cmd_class) + ['\n'])
258
def sub_help(self, cmd_class):
260
cmd_obj = cmd_class()
263
usage = command_usage(cmd_obj)
264
usage = usage.replace('bzr shelf-', '')
265
text.append('%s%s\n' % (indent, usage))
267
text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
269
# Somewhat copied from bzrlib.help.help_on_command_options
271
for option_name, option in sorted(cmd_obj.options().items()):
272
if option_name == 'help':
274
option_help.append('%s--%s' % (3 * indent, option_name))
275
if option.type is not None:
276
option_help.append(' %s' % option.argname.upper())
277
if option.short_name():
278
option_help.append(', -%s' % option.short_name())
279
option_help.append('%s%s\n' % (2 * indent, option.help))
281
if len(option_help) > 0:
282
text.append('%soptions:\n' % (2 * indent))
283
text.extend(option_help)
289
class cmd_unshelve(bzrlib.commands.Command):
290
"""Restore shelved changes.
292
By default the most recently shelved changes are restored. However if you
293
specify a patch by name those changes will be restored instead.
295
See 'shelve' for more information.
298
Option('all', help='Unshelve all changes without prompting'),
299
Option('force', help='Force unshelving even if errors occur'),
301
takes_args = ['patch?']
302
def run(self, patch=None, all=False, force=False):
303
source = BzrPatchSource()
304
s = Shelf(source.base)
305
s.unshelve(source, patch, all, force)
309
class cmd_shell(bzrlib.commands.Command):
310
"""Begin an interactive shell tailored for bzr.
311
Bzr commands can be used without typing bzr first, and will be run natively
312
when possible. Tab completion is tailored for bzr. The shell prompt shows
313
the branch nick, revno, and path.
315
If it encounters any moderately complicated shell command, it will punt to
320
bzr bzrtools:287/> status
323
bzr bzrtools:287/> status --[TAB][TAB]
324
--all --help --revision --show-ids
325
bzr bzrtools:287/> status --
329
return shell.run_shell()
331
class cmd_branch_history(bzrlib.commands.Command):
333
Display the development history of a branch.
335
Each different committer or branch nick is considered a different line of
336
development. Committers are treated as the same if they have the same
337
name, or if they have the same email address.
339
takes_args = ["branch?"]
340
def run(self, branch=None):
341
from branchhistory import branch_history
342
return branch_history(branch)
345
class cmd_zap(bzrlib.commands.Command):
347
Remove a lightweight checkout, if it can be done safely.
349
This command will remove a lightweight checkout without losing data. That
350
means it only removes lightweight checkouts, and only if they have no
353
If --branch is specified, the branch will be deleted too, but only if the
354
the branch has no new commits (relative to its parent).
356
takes_options = [Option("branch", help="Remove associtated branch from"
358
takes_args = ["checkout"]
359
def run(self, checkout, branch=False):
361
return zap(checkout, remove_branch=branch)
364
class cmd_cbranch(bzrlib.commands.Command):
366
Create a new checkout, associated with a new repository branch.
368
When you cbranch, bzr looks up the repository associated with your current
369
directory in branches.conf. It creates a new branch in that repository
370
with the same name and relative path as the checkout you request.
372
The branches.conf parameter is "cbranch_root". So if you want
373
cbranch operations in /home/jrandom/bigproject to produce branches in
374
/home/jrandom/bigproject/repository, you'd add this:
376
[/home/jrandom/bigproject]
377
cbranch_root = /home/jrandom/bigproject/repository
379
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
380
standalone branches will be produced. Standalone branches will also
381
be produced if the source branch is in 0.7 format (or earlier).
383
takes_options = [Option("lightweight",
384
help="Create a lightweight checkout"), 'revision']
385
takes_args = ["source", "target?"]
386
def run(self, source, target=None, lightweight=False, revision=None):
387
from cbranch import cbranch
388
return cbranch(source, target, lightweight=lightweight,
392
class cmd_branches(bzrlib.commands.Command):
393
"""Scan a location for branches"""
394
takes_args = ["location?"]
395
def run(self, location=None):
396
from branches import branches
397
return branches(location)
400
class cmd_multi_pull(bzrlib.commands.Command):
401
"""Pull all the branches under a location, e.g. a repository.
403
Both branches present in the directory and the branches of checkouts are
406
takes_args = ["location?"]
407
def run(self, location=None):
408
from bzrlib.branch import Branch
409
from bzrlib.transport import get_transport
410
from bzrtools import iter_branch_tree
413
t = get_transport(location)
415
print "Can't list this type of location."
417
for branch, wt in iter_branch_tree(t):
422
parent = branch.get_parent()
429
if base.startswith(t.base):
430
relpath = base[len(t.base):].rstrip('/')
433
print "Pulling %s from %s" % (relpath, parent)
435
pullable.pull(Branch.open(parent))
440
class cmd_branch_mark(bzrlib.commands.Command):
442
Add, view or list branch markers <EXPERIMENTAL>
444
To add a mark, do 'bzr branch-mark MARK'.
445
To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
447
To delete a mark, do 'bzr branch-mark --delete MARK'
449
These marks can be used to track a branch's status.
451
takes_args = ['mark?', 'branch?']
452
takes_options = [Option('delete', help='Delete this mark')]
453
def run(self, mark=None, branch=None, delete=False):
454
from branch_mark import branch_mark
455
branch_mark(mark, branch, delete)
457
class cmd_import(bzrlib.commands.Command):
458
"""Import sources from a tarball
460
This command will import a tarball into a bzr tree, replacing any versioned
461
files already present. If a directory is specified, it is used as the
462
target. If the directory does not exist, or is not versioned, it is
465
Tarballs may be gzip or bzip2 compressed. This is autodetected.
467
If the tarball has a single root directory, that directory is stripped
468
when extracting the tarball.
471
takes_args = ['source', 'tree?']
472
def run(self, source, tree=None):
473
from upstream_import import do_import
474
do_import(source, tree)
476
class cmd_shove(bzrlib.commands.Command):
477
"""Apply uncommitted changes to another tree
479
This is useful when you start to make changes in one tree, then realize
480
they should really be done in a different tree.
482
Shove is implemented using merge, so:
483
- All changes, including renames and adds, will be applied.
484
- No changes that have already been applied will be applied.
485
- If the target is significantly different from the source, conflicts may
489
takes_args = ['target', 'source?']
490
def run(self, target, source='.'):
491
from shove import do_shove
492
do_shove(source, target)
494
class cmd_cdiff(bzrlib.commands.Command):
495
"""A color version of bzr's diff"""
496
takes_args = property(lambda x: get_cmd_object('diff').takes_args)
497
takes_options = property(lambda x: get_cmd_object('diff').takes_options)
498
def run(*args, **kwargs):
499
from colordiff import colordiff
500
colordiff(*args, **kwargs)
502
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
503
cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
504
cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches,
505
cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
509
commands.append(rspush.cmd_rspush)
511
from errors import NoPyBaz
514
commands.append(baz_import.cmd_baz_import_branch)
515
commands.append(baz_import.cmd_baz_import)
518
class cmd_baz_import_branch(bzrlib.commands.Command):
519
"""Disabled. (Requires PyBaz)"""
520
takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
521
takes_options = ['verbose', Option('max-count', type=int)]
522
def run(self, to_location=None, from_branch=None, fast=False,
523
max_count=None, verbose=False, dry_run=False,
524
reuse_history_list=[]):
525
print "This command is disabled. Please install PyBaz."
528
class cmd_baz_import(bzrlib.commands.Command):
529
"""Disabled. (Requires PyBaz)"""
530
takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
531
takes_options = ['verbose', Option('prefixes', type=str,
532
help="Prefixes of branches to import")]
533
def run(self, to_root_dir=None, from_archive=None, verbose=False,
534
reuse_history_list=[], prefixes=None):
535
print "This command is disabled. Please install PyBaz."
536
commands.extend((cmd_baz_import_branch, cmd_baz_import))
539
if hasattr(bzrlib.commands, 'register_command'):
540
for command in commands:
541
bzrlib.commands.register_command(command)
545
from bzrlib.tests.TestUtil import TestLoader
547
from doctest import DocTestSuite, ELLIPSIS
548
from unittest import TestSuite
549
import tests.clean_tree
550
import upstream_import
552
import tests.blackbox
553
import tests.shelf_tests
555
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
556
result.addTest(tests.clean_tree.test_suite())
559
result.addTest(DocTestSuite(baz_import))
562
result.addTest(tests.test_suite())
563
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
564
result.addTest(tests.blackbox.test_suite())
565
result.addTest(upstream_import.test_suite())
566
result.addTest(zap.test_suite())