3
Various useful plugins for working with bzr.
7
from errors import CommandError
8
from patchsource import BzrPatchSource
9
from shelf import Shelf
12
from bzrlib.option import Option
14
from bzrlib.errors import BzrCommandError
15
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
17
from bzrlib import DEFAULT_IGNORE
20
DEFAULT_IGNORE.append('./.shelf')
21
DEFAULT_IGNORE.append('./.bzr-shelf*')
24
Option.OPTIONS['ignored'] = Option('ignored',
25
help='delete all ignored files.')
26
Option.OPTIONS['detritus'] = Option('detritus',
27
help='delete conflict files merge backups, and failed selftest dirs.' +
28
'(*.THIS, *.BASE, *.OTHER, *~, *.tmp')
29
Option.OPTIONS['dry-run'] = Option('dry-run',
30
help='show files to delete instead of deleting them.')
32
class cmd_clean_tree(bzrlib.commands.Command):
33
"""Remove unwanted files from working tree. <BZRTOOLS>
34
Normally, ignored files are left alone.
36
takes_options = ['ignored', 'detritus', 'dry-run']
37
def run(self, ignored=False, detritus=False, dry_run=False):
38
from clean_tree import clean_tree
39
clean_tree('.', ignored=ignored, detritus=detritus, dry_run=dry_run)
41
Option.OPTIONS['merge-branch'] = Option('merge-branch',type=str)
43
class cmd_graph_ancestry(bzrlib.commands.Command):
44
"""Produce ancestry graphs using dot. <BZRTOOLS>
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. <BZRTOOLS>
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['strip'] = Option('strip', type=int, help=strip_help)
109
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
110
help="""Handle extra bzr tags""")
111
class cmd_patch(bzrlib.commands.Command):
112
"""Apply a named patch to the current tree. <BZRTOOLS>
114
takes_args = ['filename?']
115
takes_options = ['strip','bzrdiff']
116
def run(self, filename=None, strip=-1, bzrdiff=0):
117
from patch import patch
118
from bzrlib.workingtree import WorkingTree
119
wt = WorkingTree.open_containing('.')[0]
121
if bzrdiff: strip = 0
124
return patch(wt, filename, strip, legacy= not bzrdiff)
126
class cmd_shelve(bzrlib.commands.Command):
127
"""Temporarily set aside some changes from the current tree. <BZRTOOLS>
129
Shelve allows you to temporarily put changes you've made "on the shelf",
130
ie. out of the way, until a later time when you can bring them back from
131
the shelf with the 'unshelve' command.
133
Shelve is intended to help separate several sets of text changes that have
134
been inappropriately mingled. If you just want to get rid of all changes
135
(text and otherwise) and you don't need to restore them later, use revert.
136
If you want to shelve all text changes at once, use shelve --all.
138
By default shelve asks you what you want to shelve, press '?' at the
139
prompt to get help. To shelve everything run shelve --all.
141
You can put multiple items on the shelf, each time you run unshelve the
142
most recently shelved changes will be reinstated.
144
If filenames are specified, only the changes to those files will be
145
shelved, other files will be left untouched.
147
If a revision is specified, changes since that revision will be shelved.
150
takes_args = ['file*']
151
takes_options = ['message', 'revision',
152
Option('all', help='Shelve all changes without prompting')]
154
def run(self, all=False, file_list=None, message=None, revision=None):
155
if revision is not None and revision:
156
if len(revision) == 1:
157
revision = revision[0]
159
raise CommandError("shelve only accepts a single revision "
162
source = BzrPatchSource(revision, file_list)
163
s = Shelf(source.base)
164
s.shelve(source, all, message)
167
class cmd_shelf(bzrlib.commands.Command):
168
"""Perform various operations on your shelved patches. See also shelve.
171
list (ls) List the patches on the current shelf.
172
delete (del) <patch> Delete a patch from the current shelf.
173
switch <shelf> Switch to the named shelf, create it if necessary.
174
show <patch> Show the contents of the specified patch.
175
upgrade Upgrade old format shelves.
177
takes_args = ['subcommand', 'args*']
179
def run(self, subcommand, args_list):
182
source = BzrPatchSource()
183
s = Shelf(source.base)
185
if subcommand == 'ls' or subcommand == 'list':
186
self.__check_no_args(args_list, "shelf list takes no arguments!")
188
elif subcommand == 'delete' or subcommand == 'del':
189
self.__check_one_arg(args_list, "shelf delete takes one argument!")
190
s.delete(args_list[0])
191
elif subcommand == 'switch':
192
self.__check_one_arg(args_list, "shelf switch takes one argument!")
193
s = Shelf(source.base, args_list[0])
195
elif subcommand == 'show':
196
self.__check_one_arg(args_list, "shelf show takes one argument!")
197
s.display(args_list[0])
198
elif subcommand == 'upgrade':
199
self.__check_no_args(args_list, "shelf upgrade takes no arguments!")
202
print subcommand, args_list
203
print >>sys.stderr, "Unknown shelf subcommand '%s'" % subcommand
205
def __check_one_arg(self, args, msg):
206
if args is None or len(args) != 1:
207
raise CommandError(msg)
209
def __check_no_args(self, args, msg):
211
raise CommandError(msg)
214
class cmd_unshelve(bzrlib.commands.Command):
215
"""Restore the most recently shelved changes to current tree. <BZRTOOLS>
216
See 'shelve' for more information.
219
Option('all', help='Unshelve all changes without prompting'),
220
Option('force', help='Force unshelving even if errors occur'),
222
def run(self, all=False, force=False):
223
source = BzrPatchSource()
224
s = Shelf(source.base)
225
s.unshelve(source, all, force)
229
class cmd_shell(bzrlib.commands.Command):
230
"""Begin an interactive shell tailored for bzr. <BZRTOOLS>
231
Bzr commands can be used without typing bzr first, and will be run natively
232
when possible. Tab completion is tailored for bzr. The shell prompt shows
233
the branch nick, revno, and path.
235
If it encounters any moderately complicated shell command, it will punt to
240
bzr bzrtools:287/> status
243
bzr bzrtools:287/> status --[TAB][TAB]
244
--all --help --revision --show-ids
245
bzr bzrtools:287/> status --
249
return shell.run_shell()
251
class cmd_branch_history(bzrlib.commands.Command):
253
Display the development history of a branch <BZRTOOLS>.
255
Each different committer or branch nick is considered a different line of
256
development. Committers are treated as the same if they have the same
257
name, or if they have the same email address.
259
takes_args = ["branch?"]
260
def run(self, branch=None):
261
from branchhistory import branch_history
262
return branch_history(branch)
265
class cmd_zap(bzrlib.commands.Command):
267
Remove a checkout, if it can be done safely. <BZRTOOLS>
269
This command will remove a checkout without losing data. That means
270
it only removes checkouts, and only if they have no uncommitted changes.
272
takes_options = [Option("branch", help="Remove associtated branch from"
274
takes_args = ["checkout"]
275
def run(self, checkout, branch=False):
277
return zap(checkout, remove_branch=branch)
280
class cmd_cbranch(bzrlib.commands.Command):
282
Create a new checkout, associated with a new repository branch. <BZRTOOLS>
284
When you cbranch, bzr looks up the repository associated with your current
285
directory in branches.conf. It creates a new branch in that repository
286
with the same name and relative path as the checkout you request.
288
The branches.conf parameter is "cbranch_root". So if you want
289
cbranch operations in /home/jrandom/bigproject to produce branches in
290
/home/jrandom/bigproject/repository, you'd add this:
292
[/home/jrandom/bigproject]
293
cbranch_root = /home/jrandom/bigproject/repository
295
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
296
standalone branches will be produced. Standalone branches will also
297
be produced if the source branch is in 0.7 format (or earlier).
299
takes_options = [Option("lightweight",
300
help="Create a lightweight checkout")]
301
takes_args = ["source", "target?"]
302
def run(self, source, target=None, lightweight=False):
303
from cbranch import cbranch
304
return cbranch(source, target, lightweight=lightweight)
307
class cmd_branches(bzrlib.commands.Command):
308
"""Scan a location for branches <BZRTOOLS>"""
309
takes_args = ["location?"]
310
def run(self, location=None):
311
from branches import branches
312
return branches(location)
315
class cmd_multi_pull(bzrlib.commands.Command):
316
"""Pull all the branches under a location, e.g. a repository. <BZRTOOLS>
318
Both branches present in the directory and the branches of checkouts are
321
takes_args = ["location?"]
322
def run(self, location=None):
323
from bzrlib.branch import Branch
324
from bzrlib.transport import get_transport
325
from bzrtools import iter_branch_tree
328
t = get_transport(location)
330
print "Can't list this type of location."
332
for branch, wt in iter_branch_tree(t):
337
parent = branch.get_parent()
344
if base.startswith(t.base):
345
relpath = base[len(t.base):].rstrip('/')
348
print "Pulling %s from %s" % (relpath, parent)
350
pullable.pull(Branch.open(parent))
355
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
356
cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
357
cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches,
361
command_decorators = []
364
import bzrlib.builtins
365
if not hasattr(bzrlib.builtins, "cmd_push"):
366
commands.append(push.cmd_push)
368
command_decorators.append(push.cmd_push)
370
from errors import NoPyBaz
373
commands.append(baz_import.cmd_baz_import_branch)
374
commands.append(baz_import.cmd_baz_import)
377
class cmd_baz_import_branch(bzrlib.commands.Command):
378
"""Disabled. (Requires PyBaz) <BZRTOOLS>"""
379
takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
380
takes_options = ['verbose', Option('max-count', type=int)]
381
def run(self, to_location=None, from_branch=None, fast=False,
382
max_count=None, verbose=False, dry_run=False,
383
reuse_history_list=[]):
384
print "This command is disabled. Please install PyBaz."
387
class cmd_baz_import(bzrlib.commands.Command):
388
"""Disabled. (Requires PyBaz) <BZRTOOLS>"""
389
takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
390
takes_options = ['verbose', Option('prefixes', type=str,
391
help="Prefixes of branches to import")]
392
def run(self, to_root_dir=None, from_archive=None, verbose=False,
393
reuse_history_list=[], prefixes=None):
394
print "This command is disabled. Please install PyBaz."
395
commands.extend((cmd_baz_import_branch, cmd_baz_import))
398
if hasattr(bzrlib.commands, 'register_command'):
399
for command in commands:
400
bzrlib.commands.register_command(command)
401
for command in command_decorators:
402
command._original_command = bzrlib.commands.register_command(
408
from bzrlib.tests.TestUtil import TestLoader
410
from doctest import DocTestSuite, ELLIPSIS
411
from unittest import TestSuite
414
import tests.blackbox
415
import tests.shelf_tests
417
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
418
result.addTest(clean_tree.test_suite())
419
result.addTest(DocTestSuite(baz_import))
420
result.addTest(tests.test_suite())
421
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
422
result.addTest(tests.blackbox.test_suite())
423
result.addTest(zap.test_suite())