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.errors import BzrCommandError
18
from bzrlib.help import command_usage
19
from bzrlib.option import Option
20
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
24
DEFAULT_IGNORE.append('./.shelf')
25
DEFAULT_IGNORE.append('./.bzr-shelf*')
28
class cmd_clean_tree(bzrlib.commands.Command):
29
"""Remove unwanted files from working tree. <BZRTOOLS>
30
Normally, ignored files are left alone.
32
takes_options = [Option('ignored', help='delete all ignored files.'),
33
Option('detritus', help='delete conflict files merge'
34
' backups, and failed selftest dirs. (*.THIS, '
35
'*.BASE, *.OTHER, *~, *.tmp)'),
36
Option('dry-run', help='show files to delete instead of'
38
def run(self, ignored=False, detritus=False, dry_run=False):
39
from clean_tree import clean_tree
40
clean_tree('.', ignored=ignored, detritus=detritus, dry_run=dry_run)
42
class cmd_graph_ancestry(bzrlib.commands.Command):
43
"""Produce ancestry graphs using dot. <BZRTOOLS>
45
Output format is detected according to file extension. Some of the more
46
common output formats are html, png, gif, svg, ps. An extension of '.dot'
47
will cause a dot graph file to be produced. HTML output has mouseovers
48
that show the commit message.
50
Branches are labeled r?, where ? is the revno. If they have no revno,
51
with the last 5 characters of their revision identifier are used instead.
53
The value starting with d is "(maximum) distance from the null revision".
55
If --merge-branch is specified, the two branches are compared and a merge
63
blue COMMON non-history ancestor
64
green Merge base (COMMON ancestor farthest from the null revision)
65
dotted Ghost revision (missing from branch storage)
67
Ancestry is usually collapsed by skipping revisions with a single parent
68
and descendant. The number of skipped revisions is shown on the arrow.
69
This feature can be disabled with --no-collapse.
71
By default, revisions are ordered by distance from root, but they can be
72
clustered instead using --cluster.
74
If available, rsvg is used to antialias PNG and JPEG output, but this can
75
be disabled with --no-antialias.
77
takes_args = ['branch', 'file']
78
takes_options = [Option('no-collapse', help="Do not skip simple nodes"),
79
Option('no-antialias',
80
help="Do not use rsvg to produce antialiased output"),
81
Option('merge-branch', type=str,
82
help="Use this branch to calcuate a merge base"),
83
Option('cluster', help="Use clustered output.")]
84
def run(self, branch, file, no_collapse=False, no_antialias=False,
85
merge_branch=None, cluster=False):
91
graph.write_ancestry_file(branch, file, not no_collapse,
92
not no_antialias, merge_branch, ranking)
94
class cmd_fetch_ghosts(bzrlib.commands.Command):
95
"""Attempt to retrieve ghosts from another branch. <BZRTOOLS>
96
If the other branch is not supplied, the last-pulled branch is used.
98
aliases = ['fetch-missing']
99
takes_args = ['branch?']
100
takes_options = [Option('no-fix')]
101
def run(self, branch=None, no_fix=False):
102
from fetch_ghosts import fetch_ghosts
103
fetch_ghosts(branch, no_fix)
105
strip_help="""Strip the smallest prefix containing num leading slashes from \
106
each file name found in the patch file."""
107
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
108
help="""Handle extra bzr tags""")
109
class cmd_patch(bzrlib.commands.Command):
110
"""Apply a named patch to the current tree. <BZRTOOLS>
112
takes_args = ['filename?']
113
takes_options = [Option('strip', type=int, help=strip_help)]
114
def run(self, filename=None, strip=-1, bzrdiff=0):
115
from patch import patch
116
from bzrlib.workingtree import WorkingTree
117
wt = WorkingTree.open_containing('.')[0]
119
if bzrdiff: strip = 0
122
return patch(wt, filename, strip, legacy= not bzrdiff)
124
class cmd_shelve(bzrlib.commands.Command):
125
"""Temporarily set aside some changes from the current tree. <BZRTOOLS>
127
Shelve allows you to temporarily put changes you've made "on the shelf",
128
ie. out of the way, until a later time when you can bring them back from
129
the shelf with the 'unshelve' command.
131
Shelve is intended to help separate several sets of text changes that have
132
been inappropriately mingled. If you just want to get rid of all changes
133
(text and otherwise) and you don't need to restore them later, use revert.
134
If you want to shelve all text changes at once, use shelve --all.
136
By default shelve asks you what you want to shelve, press '?' at the
137
prompt to get help. To shelve everything run shelve --all.
139
If filenames are specified, only the changes to those files will be
140
shelved, other files will be left untouched.
142
If a revision is specified, changes since that revision will be shelved.
144
You can put multiple items on the shelf. Normally each time you run
145
unshelve the most recently shelved changes will be reinstated. However,
146
you can also unshelve changes in a different order by explicitly
147
specifiying which changes to unshelve. This works best when the changes
148
don't depend on each other.
151
takes_args = ['file*']
152
takes_options = ['message', 'revision',
153
Option('all', help='Shelve all changes without prompting')]
155
def run(self, all=False, file_list=None, message=None, revision=None):
156
if revision is not None and revision:
157
if len(revision) == 1:
158
revision = revision[0]
160
raise CommandError("shelve only accepts a single revision "
163
source = BzrPatchSource(revision, file_list)
164
s = Shelf(source.base)
165
s.shelve(source, all, message)
169
# The following classes are only used as subcommands for 'shelf', they're
170
# not to be registered directly with bzr.
172
class cmd_shelf_list(bzrlib.commands.Command):
173
"""List the patches on the current shelf."""
174
aliases = ['list', 'ls']
179
class cmd_shelf_delete(bzrlib.commands.Command):
180
"""Delete the patch from the current shelf."""
181
aliases = ['delete', 'del']
182
takes_args = ['patch']
183
def run(self, patch):
184
self.shelf.delete(patch)
187
class cmd_shelf_switch(bzrlib.commands.Command):
188
"""Switch to the other shelf, create it if necessary."""
190
takes_args = ['othershelf']
191
def run(self, othershelf):
192
s = Shelf(self.shelf.base, othershelf)
196
class cmd_shelf_show(bzrlib.commands.Command):
197
"""Show the contents of the specified or topmost patch."""
198
aliases = ['show', 'cat', 'display']
199
takes_args = ['patch?']
200
def run(self, patch=None):
201
self.shelf.display(patch)
204
class cmd_shelf_upgrade(bzrlib.commands.Command):
205
"""Upgrade old format shelves."""
206
aliases = ['upgrade']
211
class cmd_shelf(bzrlib.commands.Command):
212
"""Perform various operations on your shelved patches. See also shelve."""
213
takes_args = ['subcommand', 'args*']
215
subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
216
cmd_shelf_show, cmd_shelf_upgrade]
218
def run(self, subcommand, args_list):
221
cmd = self._get_cmd_object(subcommand)
222
source = BzrPatchSource()
223
s = Shelf(source.base)
225
return cmd.run_argv_aliases(args_list)
227
def _get_cmd_object(self, cmd_name):
228
for cmd_class in self.subcommands:
229
for alias in cmd_class.aliases:
230
if alias == cmd_name:
232
raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
235
text = ["%s\n\nSubcommands:\n" % self.__doc__]
237
for cmd_class in self.subcommands:
238
text.extend(self.sub_help(cmd_class) + ['\n'])
242
def sub_help(self, cmd_class):
244
cmd_obj = cmd_class()
247
usage = command_usage(cmd_obj)
248
usage = usage.replace('bzr shelf-', '')
249
text.append('%s%s\n' % (indent, usage))
251
text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
253
# Somewhat copied from bzrlib.help.help_on_command_options
255
for option_name, option in sorted(cmd_obj.options().items()):
256
if option_name == 'help':
258
option_help.append('%s--%s' % (3 * indent, option_name))
259
if option.type is not None:
260
option_help.append(' %s' % option.argname.upper())
261
if option.short_name():
262
option_help.append(', -%s' % option.short_name())
263
option_help.append('%s%s\n' % (2 * indent, option.help))
265
if len(option_help) > 0:
266
text.append('%soptions:\n' % (2 * indent))
267
text.extend(option_help)
273
class cmd_unshelve(bzrlib.commands.Command):
274
"""Restore shelved changes. <BZRTOOLS>
276
By default the most recently shelved changes are restored. However if you
277
specify a patch by name those changes will be restored instead.
279
See 'shelve' for more information.
282
Option('all', help='Unshelve all changes without prompting'),
283
Option('force', help='Force unshelving even if errors occur'),
285
takes_args = ['patch?']
286
def run(self, patch=None, all=False, force=False):
287
source = BzrPatchSource()
288
s = Shelf(source.base)
289
s.unshelve(source, patch, all, force)
293
class cmd_shell(bzrlib.commands.Command):
294
"""Begin an interactive shell tailored for bzr. <BZRTOOLS>
295
Bzr commands can be used without typing bzr first, and will be run natively
296
when possible. Tab completion is tailored for bzr. The shell prompt shows
297
the branch nick, revno, and path.
299
If it encounters any moderately complicated shell command, it will punt to
304
bzr bzrtools:287/> status
307
bzr bzrtools:287/> status --[TAB][TAB]
308
--all --help --revision --show-ids
309
bzr bzrtools:287/> status --
313
return shell.run_shell()
315
class cmd_branch_history(bzrlib.commands.Command):
317
Display the development history of a branch <BZRTOOLS>.
319
Each different committer or branch nick is considered a different line of
320
development. Committers are treated as the same if they have the same
321
name, or if they have the same email address.
323
takes_args = ["branch?"]
324
def run(self, branch=None):
325
from branchhistory import branch_history
326
return branch_history(branch)
329
class cmd_zap(bzrlib.commands.Command):
331
Remove a checkout, if it can be done safely. <BZRTOOLS>
333
This command will remove a checkout without losing data. That means
334
it only removes checkouts, and only if they have no uncommitted changes.
336
takes_options = [Option("branch", help="Remove associtated branch from"
338
takes_args = ["checkout"]
339
def run(self, checkout, branch=False):
341
return zap(checkout, remove_branch=branch)
344
class cmd_cbranch(bzrlib.commands.Command):
346
Create a new checkout, associated with a new repository branch. <BZRTOOLS>
348
When you cbranch, bzr looks up the repository associated with your current
349
directory in branches.conf. It creates a new branch in that repository
350
with the same name and relative path as the checkout you request.
352
The branches.conf parameter is "cbranch_root". So if you want
353
cbranch operations in /home/jrandom/bigproject to produce branches in
354
/home/jrandom/bigproject/repository, you'd add this:
356
[/home/jrandom/bigproject]
357
cbranch_root = /home/jrandom/bigproject/repository
359
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
360
standalone branches will be produced. Standalone branches will also
361
be produced if the source branch is in 0.7 format (or earlier).
363
takes_options = [Option("lightweight",
364
help="Create a lightweight checkout")]
365
takes_args = ["source", "target?"]
366
def run(self, source, target=None, lightweight=False):
367
from cbranch import cbranch
368
return cbranch(source, target, lightweight=lightweight)
371
class cmd_branches(bzrlib.commands.Command):
372
"""Scan a location for branches <BZRTOOLS>"""
373
takes_args = ["location?"]
374
def run(self, location=None):
375
from branches import branches
376
return branches(location)
379
class cmd_multi_pull(bzrlib.commands.Command):
380
"""Pull all the branches under a location, e.g. a repository. <BZRTOOLS>
382
Both branches present in the directory and the branches of checkouts are
385
takes_args = ["location?"]
386
def run(self, location=None):
387
from bzrlib.branch import Branch
388
from bzrlib.transport import get_transport
389
from bzrtools import iter_branch_tree
392
t = get_transport(location)
394
print "Can't list this type of location."
396
for branch, wt in iter_branch_tree(t):
401
parent = branch.get_parent()
408
if base.startswith(t.base):
409
relpath = base[len(t.base):].rstrip('/')
412
print "Pulling %s from %s" % (relpath, parent)
414
pullable.pull(Branch.open(parent))
419
class cmd_branch_mark(bzrlib.commands.Command):
421
Add, view or list branch markers <EXPERIMENTAL>
423
To add a mark, do 'bzr branch-mark MARK'.
424
To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
426
To delete a mark, do 'bzr branch-mark --delete MARK'
428
These marks can be used to track a branch's status.
430
takes_args = ['mark?', 'branch?']
431
takes_options = [Option('delete', help='Delete this mark')]
432
def run(self, mark=None, branch=None, delete=False):
433
from branch_mark import branch_mark
434
branch_mark(mark, branch, delete)
436
class cmd_import(bzrlib.commands.Command):
437
"""Import sources from a tarball <BZRTOOLS>
439
This command will import a tarball into a bzr tree, replacing any versioned
440
files already present. If a directory is specified, it is used as the
441
target. If the directory does not exist, or is not versioned, it is
444
Tarballs may be gzip or bzip2 compressed. This is autodetected.
446
If the tarball has a single root directory, that directory is stripped
447
when extracting the tarball.
450
takes_args = ['source', 'tree?']
451
def run(self, source, tree=None):
452
from upstream_import import do_import
453
do_import(source, tree)
455
class cmd_shove(bzrlib.commands.Command):
456
"""Apply uncommitted changes to another tree <BZRTOOLS>
458
This is useful when you start to make changes in one tree, then realize
459
they should really be done in a different tree.
461
Shove is implemented using merge, so:
462
- All changes, including renames and adds, will be applied.
463
- No changes that have already been applied will be applied.
464
- If the target is significantly different from the source, conflicts may
468
takes_args = ['source?', 'target']
469
def run(self, target, source='.'):
470
from shove import do_shove
471
do_shove(source, target)
473
class cmd_cdiff(bzrlib.commands.Command):
474
"""A color version of bzr's diff <BZRTOOLS>"""
475
takes_args = bzrlib.builtins.cmd_diff.takes_args
476
takes_options = bzrlib.builtins.cmd_diff.takes_options
477
def run(*args, **kwargs):
478
from colordiff import colordiff
479
colordiff(*args, **kwargs)
481
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
482
cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
483
cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches,
484
cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
488
commands.append(rspush.cmd_rspush)
490
from errors import NoPyBaz
493
commands.append(baz_import.cmd_baz_import_branch)
494
commands.append(baz_import.cmd_baz_import)
497
class cmd_baz_import_branch(bzrlib.commands.Command):
498
"""Disabled. (Requires PyBaz) <BZRTOOLS>"""
499
takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
500
takes_options = ['verbose', Option('max-count', type=int)]
501
def run(self, to_location=None, from_branch=None, fast=False,
502
max_count=None, verbose=False, dry_run=False,
503
reuse_history_list=[]):
504
print "This command is disabled. Please install PyBaz."
507
class cmd_baz_import(bzrlib.commands.Command):
508
"""Disabled. (Requires PyBaz) <BZRTOOLS>"""
509
takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
510
takes_options = ['verbose', Option('prefixes', type=str,
511
help="Prefixes of branches to import")]
512
def run(self, to_root_dir=None, from_archive=None, verbose=False,
513
reuse_history_list=[], prefixes=None):
514
print "This command is disabled. Please install PyBaz."
515
commands.extend((cmd_baz_import_branch, cmd_baz_import))
518
if hasattr(bzrlib.commands, 'register_command'):
519
for command in commands:
520
bzrlib.commands.register_command(command)
524
from bzrlib.tests.TestUtil import TestLoader
526
from doctest import DocTestSuite, ELLIPSIS
527
from unittest import TestSuite
528
import tests.clean_tree
529
import upstream_import
531
import tests.blackbox
532
import tests.shelf_tests
534
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
535
result.addTest(tests.clean_tree.test_suite())
538
result.addTest(DocTestSuite(baz_import))
541
result.addTest(tests.test_suite())
542
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
543
result.addTest(tests.blackbox.test_suite())
544
result.addTest(upstream_import.test_suite())
545
result.addTest(zap.test_suite())