3
Various useful plugins for working with bzr.
2
"""Shelf - temporarily set aside changes, then bring them back."""
6
5
from errors import CommandError
6
from bzrlib.option import Option
7
7
from patchsource import BzrPatchSource
8
8
from shelf import Shelf
9
from switch import cmd_switch
13
9
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
11
DEFAULT_IGNORE.append('./.shelf')
26
12
DEFAULT_IGNORE.append('./.bzr-shelf*')
29
class cmd_clean_tree(bzrlib.commands.Command):
30
"""Remove unwanted files from working tree.
31
Normally, ignored files are left alone.
33
takes_options = [Option('ignored', help='delete all ignored files.'),
34
Option('detritus', help='delete conflict files merge'
35
' backups, and failed selftest dirs. (*.THIS, '
36
'*.BASE, *.OTHER, *~, *.tmp)'),
37
Option('dry-run', help='show files to delete instead of'
39
def run(self, ignored=False, detritus=False, dry_run=False):
40
from clean_tree import clean_tree
41
clean_tree('.', ignored=ignored, detritus=detritus, dry_run=dry_run)
43
class cmd_graph_ancestry(bzrlib.commands.Command):
44
"""Produce ancestry graphs using dot.
46
Output format is detected according to file extension. Some of the more
47
common output formats are html, png, gif, svg, ps. An extension of '.dot'
48
will cause a dot graph file to be produced. HTML output has mouseovers
49
that show the commit message.
51
Branches are labeled r?, where ? is the revno. If they have no revno,
52
with the last 5 characters of their revision identifier are used instead.
54
The value starting with d is "(maximum) distance from the null revision".
56
If --merge-branch is specified, the two branches are compared and a merge
64
blue COMMON non-history ancestor
65
green Merge base (COMMON ancestor farthest from the null revision)
66
dotted Ghost revision (missing from branch storage)
68
Ancestry is usually collapsed by skipping revisions with a single parent
69
and descendant. The number of skipped revisions is shown on the arrow.
70
This feature can be disabled with --no-collapse.
72
By default, revisions are ordered by distance from root, but they can be
73
clustered instead using --cluster.
75
If available, rsvg is used to antialias PNG and JPEG output, but this can
76
be disabled with --no-antialias.
78
takes_args = ['branch', 'file']
79
takes_options = [Option('no-collapse', help="Do not skip simple nodes"),
80
Option('no-antialias',
81
help="Do not use rsvg to produce antialiased output"),
82
Option('merge-branch', type=str,
83
help="Use this branch to calcuate a merge base"),
84
Option('cluster', help="Use clustered output.")]
85
def run(self, branch, file, no_collapse=False, no_antialias=False,
86
merge_branch=None, cluster=False):
92
graph.write_ancestry_file(branch, file, not no_collapse,
93
not no_antialias, merge_branch, ranking)
95
class cmd_fetch_ghosts(bzrlib.commands.Command):
96
"""Attempt to retrieve ghosts from another branch.
97
If the other branch is not supplied, the last-pulled branch is used.
99
aliases = ['fetch-missing']
100
takes_args = ['branch?']
101
takes_options = [Option('no-fix')]
102
def run(self, branch=None, no_fix=False):
103
from fetch_ghosts import fetch_ghosts
104
fetch_ghosts(branch, no_fix)
106
strip_help="""Strip the smallest prefix containing num leading slashes from \
107
each file name found in the patch file."""
108
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
109
help="""Handle extra bzr tags""")
110
class cmd_patch(bzrlib.commands.Command):
111
"""Apply a named patch to the current tree.
113
takes_args = ['filename?']
114
takes_options = [Option('strip', type=int, help=strip_help)]
115
def run(self, filename=None, strip=-1, bzrdiff=0):
116
from patch import patch
117
from bzrlib.workingtree import WorkingTree
118
wt = WorkingTree.open_containing('.')[0]
120
if bzrdiff: strip = 0
123
return patch(wt, filename, strip, legacy= not bzrdiff)
125
14
class cmd_shelve(bzrlib.commands.Command):
126
15
"""Temporarily set aside some changes from the current tree.
166
51
s.shelve(source, all, message)
170
# The following classes are only used as subcommands for 'shelf', they're
171
# not to be registered directly with bzr.
173
class cmd_shelf_list(bzrlib.commands.Command):
174
"""List the patches on the current shelf."""
175
aliases = ['list', 'ls']
180
class cmd_shelf_delete(bzrlib.commands.Command):
181
"""Delete the patch from the current shelf."""
182
aliases = ['delete', 'del']
183
takes_args = ['patch']
184
def run(self, patch):
185
self.shelf.delete(patch)
188
class cmd_shelf_switch(bzrlib.commands.Command):
189
"""Switch to the other shelf, create it if necessary."""
191
takes_args = ['othershelf']
192
def run(self, othershelf):
193
s = Shelf(self.shelf.base, othershelf)
197
class cmd_shelf_show(bzrlib.commands.Command):
198
"""Show the contents of the specified or topmost patch."""
199
aliases = ['show', 'cat', 'display']
200
takes_args = ['patch?']
201
def run(self, patch=None):
202
self.shelf.display(patch)
205
class cmd_shelf_upgrade(bzrlib.commands.Command):
206
"""Upgrade old format shelves."""
207
aliases = ['upgrade']
212
54
class cmd_shelf(bzrlib.commands.Command):
213
"""Perform various operations on your shelved patches. See also shelve."""
55
"""Perform various operations on your shelved patches. See also shelve.
58
list (ls) List the patches on the current shelf.
59
delete (del) <patch> Delete a patch from the current shelf.
60
switch <shelf> Switch to the named shelf, create it if necessary.
61
show <patch> Show the contents of the specified patch.
62
upgrade Upgrade old format shelves.
214
64
takes_args = ['subcommand', 'args*']
216
subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
217
cmd_shelf_show, cmd_shelf_upgrade]
219
66
def run(self, subcommand, args_list):
222
cmd = self._get_cmd_object(subcommand)
223
69
source = BzrPatchSource()
224
70
s = Shelf(source.base)
226
return cmd.run_argv_aliases(args_list)
228
def _get_cmd_object(self, cmd_name):
229
for cmd_class in self.subcommands:
230
for alias in cmd_class.aliases:
231
if alias == cmd_name:
233
raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
236
text = ["%s\n\nSubcommands:\n" % self.__doc__]
238
for cmd_class in self.subcommands:
239
text.extend(self.sub_help(cmd_class) + ['\n'])
243
def sub_help(self, cmd_class):
245
cmd_obj = cmd_class()
248
usage = command_usage(cmd_obj)
249
usage = usage.replace('bzr shelf-', '')
250
text.append('%s%s\n' % (indent, usage))
252
text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
254
# Somewhat copied from bzrlib.help.help_on_command_options
256
for option_name, option in sorted(cmd_obj.options().items()):
257
if option_name == 'help':
259
option_help.append('%s--%s' % (3 * indent, option_name))
260
if option.type is not None:
261
option_help.append(' %s' % option.argname.upper())
262
if option.short_name():
263
option_help.append(', -%s' % option.short_name())
264
option_help.append('%s%s\n' % (2 * indent, option.help))
266
if len(option_help) > 0:
267
text.append('%soptions:\n' % (2 * indent))
268
text.extend(option_help)
72
if subcommand == 'ls' or subcommand == 'list':
73
self.__check_no_args(args_list, "shelf list takes no arguments!")
75
elif subcommand == 'delete' or subcommand == 'del':
76
self.__check_one_arg(args_list, "shelf delete takes one argument!")
77
s.delete(args_list[0])
78
elif subcommand == 'switch':
79
self.__check_one_arg(args_list, "shelf switch takes one argument!")
80
s = Shelf(source.base, args_list[0])
82
elif subcommand == 'show':
83
self.__check_one_arg(args_list, "shelf show takes one argument!")
84
s.display(args_list[0])
85
elif subcommand == 'upgrade':
86
self.__check_no_args(args_list, "shelf upgrade takes no arguments!")
89
raise CommandError("Unknown shelf subcommand '%s'" % subcommand)
91
def __check_one_arg(self, args, msg):
92
if args is None or len(args) != 1:
93
raise CommandError(msg)
95
def __check_no_args(self, args, msg):
97
raise CommandError(msg)
274
99
class cmd_unshelve(bzrlib.commands.Command):
275
"""Restore shelved changes.
277
By default the most recently shelved changes are restored. However if you
278
specify a patch by name those changes will be restored instead.
100
"""Restore the most recently shelved changes to the current tree.
280
101
See 'shelve' for more information.
282
103
takes_options = [
283
104
Option('all', help='Unshelve all changes without prompting'),
284
105
Option('force', help='Force unshelving even if errors occur'),
286
takes_args = ['patch?']
287
def run(self, patch=None, all=False, force=False):
107
def run(self, all=False, force=False):
288
108
source = BzrPatchSource()
289
109
s = Shelf(source.base)
290
s.unshelve(source, patch, all, force)
110
s.unshelve(source, all, force)
294
class cmd_shell(bzrlib.commands.Command):
295
"""Begin an interactive shell tailored for bzr.
296
Bzr commands can be used without typing bzr first, and will be run natively
297
when possible. Tab completion is tailored for bzr. The shell prompt shows
298
the branch nick, revno, and path.
300
If it encounters any moderately complicated shell command, it will punt to
305
bzr bzrtools:287/> status
308
bzr bzrtools:287/> status --[TAB][TAB]
309
--all --help --revision --show-ids
310
bzr bzrtools:287/> status --
314
return shell.run_shell()
316
class cmd_branch_history(bzrlib.commands.Command):
318
Display the development history of a branch.
320
Each different committer or branch nick is considered a different line of
321
development. Committers are treated as the same if they have the same
322
name, or if they have the same email address.
324
takes_args = ["branch?"]
325
def run(self, branch=None):
326
from branchhistory import branch_history
327
return branch_history(branch)
330
class cmd_zap(bzrlib.commands.Command):
332
Remove a lightweight checkout, if it can be done safely.
334
This command will remove a lightweight checkout without losing data. That
335
means it only removes lightweight checkouts, and only if they have no
338
If --branch is specified, the branch will be deleted too, but only if the
339
the branch has no new commits (relative to its parent).
341
takes_options = [Option("branch", help="Remove associtated branch from"
343
takes_args = ["checkout"]
344
def run(self, checkout, branch=False):
346
return zap(checkout, remove_branch=branch)
349
class cmd_cbranch(bzrlib.commands.Command):
351
Create a new checkout, associated with a new repository branch.
353
When you cbranch, bzr looks up the repository associated with your current
354
directory in branches.conf. It creates a new branch in that repository
355
with the same name and relative path as the checkout you request.
357
The branches.conf parameter is "cbranch_root". So if you want
358
cbranch operations in /home/jrandom/bigproject to produce branches in
359
/home/jrandom/bigproject/repository, you'd add this:
361
[/home/jrandom/bigproject]
362
cbranch_root = /home/jrandom/bigproject/repository
364
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
365
standalone branches will be produced. Standalone branches will also
366
be produced if the source branch is in 0.7 format (or earlier).
368
takes_options = [Option("lightweight",
369
help="Create a lightweight checkout")]
370
takes_args = ["source", "target?"]
371
def run(self, source, target=None, lightweight=False):
372
from cbranch import cbranch
373
return cbranch(source, target, lightweight=lightweight)
376
class cmd_branches(bzrlib.commands.Command):
377
"""Scan a location for branches"""
378
takes_args = ["location?"]
379
def run(self, location=None):
380
from branches import branches
381
return branches(location)
384
class cmd_multi_pull(bzrlib.commands.Command):
385
"""Pull all the branches under a location, e.g. a repository.
387
Both branches present in the directory and the branches of checkouts are
390
takes_args = ["location?"]
391
def run(self, location=None):
392
from bzrlib.branch import Branch
393
from bzrlib.transport import get_transport
394
from bzrtools import iter_branch_tree
397
t = get_transport(location)
399
print "Can't list this type of location."
401
for branch, wt in iter_branch_tree(t):
406
parent = branch.get_parent()
413
if base.startswith(t.base):
414
relpath = base[len(t.base):].rstrip('/')
417
print "Pulling %s from %s" % (relpath, parent)
419
pullable.pull(Branch.open(parent))
424
class cmd_branch_mark(bzrlib.commands.Command):
426
Add, view or list branch markers <EXPERIMENTAL>
428
To add a mark, do 'bzr branch-mark MARK'.
429
To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
431
To delete a mark, do 'bzr branch-mark --delete MARK'
433
These marks can be used to track a branch's status.
435
takes_args = ['mark?', 'branch?']
436
takes_options = [Option('delete', help='Delete this mark')]
437
def run(self, mark=None, branch=None, delete=False):
438
from branch_mark import branch_mark
439
branch_mark(mark, branch, delete)
441
class cmd_import(bzrlib.commands.Command):
442
"""Import sources from a tarball
444
This command will import a tarball into a bzr tree, replacing any versioned
445
files already present. If a directory is specified, it is used as the
446
target. If the directory does not exist, or is not versioned, it is
449
Tarballs may be gzip or bzip2 compressed. This is autodetected.
451
If the tarball has a single root directory, that directory is stripped
452
when extracting the tarball.
455
takes_args = ['source', 'tree?']
456
def run(self, source, tree=None):
457
from upstream_import import do_import
458
do_import(source, tree)
460
class cmd_shove(bzrlib.commands.Command):
461
"""Apply uncommitted changes to another tree
463
This is useful when you start to make changes in one tree, then realize
464
they should really be done in a different tree.
466
Shove is implemented using merge, so:
467
- All changes, including renames and adds, will be applied.
468
- No changes that have already been applied will be applied.
469
- If the target is significantly different from the source, conflicts may
473
takes_args = ['target', 'source?']
474
def run(self, target, source='.'):
475
from shove import do_shove
476
do_shove(source, target)
478
class cmd_cdiff(bzrlib.commands.Command):
479
"""A color version of bzr's diff"""
480
takes_args = property(lambda x: get_cmd_object('diff').takes_args)
481
takes_options = property(lambda x: get_cmd_object('diff').takes_options)
482
def run(*args, **kwargs):
483
from colordiff import colordiff
484
colordiff(*args, **kwargs)
486
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
487
cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
488
cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches,
489
cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
493
commands.append(rspush.cmd_rspush)
495
from errors import NoPyBaz
498
commands.append(baz_import.cmd_baz_import_branch)
499
commands.append(baz_import.cmd_baz_import)
502
class cmd_baz_import_branch(bzrlib.commands.Command):
503
"""Disabled. (Requires PyBaz)"""
504
takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
505
takes_options = ['verbose', Option('max-count', type=int)]
506
def run(self, to_location=None, from_branch=None, fast=False,
507
max_count=None, verbose=False, dry_run=False,
508
reuse_history_list=[]):
509
print "This command is disabled. Please install PyBaz."
512
class cmd_baz_import(bzrlib.commands.Command):
513
"""Disabled. (Requires PyBaz)"""
514
takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
515
takes_options = ['verbose', Option('prefixes', type=str,
516
help="Prefixes of branches to import")]
517
def run(self, to_root_dir=None, from_archive=None, verbose=False,
518
reuse_history_list=[], prefixes=None):
519
print "This command is disabled. Please install PyBaz."
520
commands.extend((cmd_baz_import_branch, cmd_baz_import))
523
if hasattr(bzrlib.commands, 'register_command'):
524
for command in commands:
525
bzrlib.commands.register_command(command)
113
bzrlib.commands.register_command(cmd_shelf)
114
bzrlib.commands.register_command(cmd_shelve)
115
bzrlib.commands.register_command(cmd_unshelve)
528
117
def test_suite():
529
118
from bzrlib.tests.TestUtil import TestLoader
531
from doctest import DocTestSuite, ELLIPSIS
532
from unittest import TestSuite
533
import tests.clean_tree
534
import upstream_import
536
import tests.blackbox
537
import tests.shelf_tests
539
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
540
result.addTest(tests.clean_tree.test_suite())
543
result.addTest(DocTestSuite(baz_import))
546
result.addTest(tests.test_suite())
547
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
548
result.addTest(tests.blackbox.test_suite())
549
result.addTest(upstream_import.test_suite())
550
result.addTest(zap.test_suite())
120
return TestLoader().loadTestsFromModule(tests)